patchright-core 1.57.0 → 1.58.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/ThirdPartyNotices.txt +3223 -308
  2. package/browsers.json +21 -22
  3. package/lib/cli/program.js +4 -5
  4. package/lib/client/api.js +3 -0
  5. package/lib/client/browser.js +3 -5
  6. package/lib/client/browserContext.js +40 -4
  7. package/lib/client/browserType.js +4 -3
  8. package/lib/client/connection.js +4 -0
  9. package/lib/client/elementHandle.js +3 -0
  10. package/lib/client/events.js +3 -0
  11. package/lib/client/fetch.js +3 -4
  12. package/lib/client/frame.js +10 -1
  13. package/lib/client/locator.js +8 -0
  14. package/lib/client/network.js +5 -1
  15. package/lib/client/page.js +29 -1
  16. package/lib/client/pageAgent.js +64 -0
  17. package/lib/client/platform.js +3 -0
  18. package/lib/client/tracing.js +1 -1
  19. package/lib/generated/injectedScriptSource.js +1 -1
  20. package/lib/generated/pollingRecorderSource.js +1 -1
  21. package/lib/mcpBundle.js +84 -0
  22. package/lib/mcpBundleImpl/index.js +147 -0
  23. package/lib/protocol/serializers.js +5 -0
  24. package/lib/protocol/validator.js +88 -4
  25. package/lib/remote/playwrightServer.js +1 -2
  26. package/lib/server/agent/actionRunner.js +335 -0
  27. package/lib/server/agent/actions.js +128 -0
  28. package/lib/server/agent/codegen.js +111 -0
  29. package/lib/server/agent/context.js +150 -0
  30. package/lib/server/agent/expectTools.js +156 -0
  31. package/lib/server/agent/pageAgent.js +204 -0
  32. package/lib/server/agent/performTools.js +262 -0
  33. package/lib/server/agent/tool.js +109 -0
  34. package/lib/server/artifact.js +1 -1
  35. package/lib/server/bidi/bidiBrowser.js +56 -12
  36. package/lib/server/bidi/bidiChromium.js +8 -12
  37. package/lib/server/bidi/bidiConnection.js +1 -0
  38. package/lib/server/bidi/bidiDeserializer.js +116 -0
  39. package/lib/server/bidi/bidiExecutionContext.js +75 -29
  40. package/lib/server/bidi/bidiFirefox.js +6 -8
  41. package/lib/server/bidi/bidiNetworkManager.js +1 -1
  42. package/lib/server/bidi/bidiPage.js +39 -28
  43. package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
  44. package/lib/server/browserContext.js +34 -26
  45. package/lib/server/browserType.js +12 -4
  46. package/lib/server/chromium/chromium.js +14 -20
  47. package/lib/server/chromium/chromiumSwitches.js +2 -2
  48. package/lib/server/chromium/crBrowser.js +22 -12
  49. package/lib/server/chromium/crConnection.js +0 -5
  50. package/lib/server/chromium/crCoverage.js +13 -1
  51. package/lib/server/chromium/crDevTools.js +0 -2
  52. package/lib/server/chromium/crNetworkManager.js +92 -12
  53. package/lib/server/chromium/crPage.js +62 -116
  54. package/lib/server/codegen/javascript.js +6 -29
  55. package/lib/server/deviceDescriptorsSource.json +56 -56
  56. package/lib/server/dispatchers/browserContextDispatcher.js +3 -2
  57. package/lib/server/dispatchers/dispatcher.js +6 -13
  58. package/lib/server/dispatchers/frameDispatcher.js +1 -1
  59. package/lib/server/dispatchers/jsHandleDispatcher.js +2 -2
  60. package/lib/server/dispatchers/pageAgentDispatcher.js +96 -0
  61. package/lib/server/dispatchers/pageDispatcher.js +4 -0
  62. package/lib/server/dom.js +12 -3
  63. package/lib/server/electron/electron.js +5 -2
  64. package/lib/server/firefox/ffBrowser.js +10 -20
  65. package/lib/server/firefox/ffConnection.js +0 -5
  66. package/lib/server/firefox/ffNetworkManager.js +2 -2
  67. package/lib/server/firefox/ffPage.js +15 -18
  68. package/lib/server/firefox/firefox.js +6 -8
  69. package/lib/server/frameSelectors.js +16 -4
  70. package/lib/server/frames.js +251 -86
  71. package/lib/server/instrumentation.js +3 -0
  72. package/lib/server/javascript.js +8 -4
  73. package/lib/server/launchApp.js +2 -1
  74. package/lib/server/network.js +50 -12
  75. package/lib/server/page.js +61 -91
  76. package/lib/server/progress.js +26 -6
  77. package/lib/server/recorder/recorderApp.js +79 -100
  78. package/lib/server/registry/browserFetcher.js +6 -4
  79. package/lib/server/registry/index.js +172 -149
  80. package/lib/server/registry/oopDownloadBrowserMain.js +3 -0
  81. package/lib/server/screencast.js +190 -0
  82. package/lib/server/screenshotter.js +6 -0
  83. package/lib/server/trace/recorder/snapshotter.js +17 -8
  84. package/lib/server/trace/recorder/snapshotterInjected.js +30 -72
  85. package/lib/server/trace/recorder/tracing.js +29 -21
  86. package/lib/server/trace/viewer/traceParser.js +72 -0
  87. package/lib/server/trace/viewer/traceViewer.js +21 -17
  88. package/lib/server/utils/expectUtils.js +87 -2
  89. package/lib/server/utils/hostPlatform.js +15 -0
  90. package/lib/server/utils/httpServer.js +5 -20
  91. package/lib/server/utils/network.js +37 -28
  92. package/lib/server/utils/nodePlatform.js +6 -0
  93. package/lib/server/{chromium/videoRecorder.js → videoRecorder.js} +22 -13
  94. package/lib/server/webkit/webkit.js +4 -6
  95. package/lib/server/webkit/wkBrowser.js +2 -6
  96. package/lib/server/webkit/wkConnection.js +1 -6
  97. package/lib/server/webkit/wkInterceptableRequest.js +29 -1
  98. package/lib/server/webkit/wkPage.js +75 -46
  99. package/lib/utils/isomorphic/ariaSnapshot.js +60 -2
  100. package/lib/utils/isomorphic/lruCache.js +51 -0
  101. package/lib/utils/isomorphic/protocolMetainfo.js +9 -1
  102. package/lib/utils/isomorphic/stringUtils.js +49 -0
  103. package/lib/utils/isomorphic/trace/entries.js +16 -0
  104. package/lib/utils/isomorphic/trace/snapshotRenderer.js +499 -0
  105. package/lib/utils/isomorphic/trace/snapshotServer.js +120 -0
  106. package/lib/utils/isomorphic/trace/snapshotStorage.js +89 -0
  107. package/lib/utils/isomorphic/trace/traceLoader.js +131 -0
  108. package/lib/utils/isomorphic/trace/traceModel.js +365 -0
  109. package/lib/utils/isomorphic/trace/traceModernizer.js +400 -0
  110. package/lib/utils/isomorphic/trace/versions/traceV3.js +16 -0
  111. package/lib/utils/isomorphic/trace/versions/traceV4.js +16 -0
  112. package/lib/utils/isomorphic/trace/versions/traceV5.js +16 -0
  113. package/lib/utils/isomorphic/trace/versions/traceV6.js +16 -0
  114. package/lib/utils/isomorphic/trace/versions/traceV7.js +16 -0
  115. package/lib/utils/isomorphic/trace/versions/traceV8.js +16 -0
  116. package/lib/utils/isomorphic/yaml.js +84 -0
  117. package/lib/utils.js +2 -0
  118. package/lib/utilsBundle.js +2 -5
  119. package/lib/utilsBundleImpl/index.js +165 -165
  120. package/lib/vite/htmlReport/index.html +21 -21
  121. package/lib/vite/recorder/assets/codeMirrorModule-CFUTFUO7.js +32 -0
  122. package/lib/vite/{traceViewer/codeMirrorModule.C3UTv-Ge.css → recorder/assets/codeMirrorModule-DYBRYzYX.css} +1 -1
  123. package/lib/vite/recorder/assets/{index-Ri0uHF7I.css → index-BSjZa4pk.css} +1 -1
  124. package/lib/vite/recorder/assets/index-CVkBxsGf.js +193 -0
  125. package/lib/vite/recorder/index.html +2 -2
  126. package/lib/vite/traceViewer/assets/codeMirrorModule-BVA4h_ZY.js +32 -0
  127. package/lib/vite/traceViewer/assets/defaultSettingsView-CjfmcdOz.js +266 -0
  128. package/lib/vite/{recorder/assets/codeMirrorModule-C3UTv-Ge.css → traceViewer/codeMirrorModule.DYBRYzYX.css} +1 -1
  129. package/lib/vite/traceViewer/defaultSettingsView.7ch9cixO.css +1 -0
  130. package/lib/vite/traceViewer/index.BVu7tZDe.css +1 -0
  131. package/lib/vite/traceViewer/index.BtyWtaE-.js +2 -0
  132. package/lib/vite/traceViewer/index.html +4 -4
  133. package/lib/vite/traceViewer/sw.bundle.js +5 -3
  134. package/lib/vite/traceViewer/uiMode.fyrXARf2.js +5 -0
  135. package/lib/vite/traceViewer/uiMode.html +3 -3
  136. package/package.json +2 -1
  137. package/types/protocol.d.ts +738 -159
  138. package/types/types.d.ts +25 -38
  139. package/lib/server/bidi/third_party/bidiDeserializer.js +0 -98
  140. package/lib/server/trace/test/inMemorySnapshotter.js +0 -87
  141. package/lib/vite/recorder/assets/codeMirrorModule-CBbSe-ZI.js +0 -25
  142. package/lib/vite/recorder/assets/index-CpZVd2nA.js +0 -193
  143. package/lib/vite/traceViewer/assets/codeMirrorModule-DHz0wP2C.js +0 -25
  144. package/lib/vite/traceViewer/assets/defaultSettingsView-WsZP88O6.js +0 -266
  145. package/lib/vite/traceViewer/defaultSettingsView.ConWv5KN.css +0 -1
  146. package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
  147. package/lib/vite/traceViewer/index.C8xAeo93.js +0 -2
  148. package/lib/vite/traceViewer/uiMode.BltraIJB.js +0 -5
@@ -47,20 +47,21 @@ var import_launchApp2 = require("../../launchApp");
47
47
  var import_playwright = require("../../playwright");
48
48
  var import_progress = require("../../progress");
49
49
  const tracesDirMarker = "traces.dir";
50
- function validateTraceUrl(traceUrl) {
51
- if (!traceUrl)
52
- return traceUrl;
53
- if (traceUrl.startsWith("http://") || traceUrl.startsWith("https://"))
54
- return traceUrl;
55
- if (traceUrl.endsWith(".json"))
56
- return traceUrl;
50
+ function validateTraceUrlOrPath(traceFileOrUrl) {
51
+ if (!traceFileOrUrl)
52
+ return traceFileOrUrl;
53
+ if (traceFileOrUrl.startsWith("http://") || traceFileOrUrl.startsWith("https://"))
54
+ return traceFileOrUrl;
55
+ let traceFile = traceFileOrUrl;
56
+ if (traceFile.endsWith(".json"))
57
+ return toFilePathUrl(traceFile);
57
58
  try {
58
- const stat = import_fs.default.statSync(traceUrl);
59
+ const stat = import_fs.default.statSync(traceFile);
59
60
  if (stat.isDirectory())
60
- return import_path.default.join(traceUrl, tracesDirMarker);
61
- return traceUrl;
61
+ traceFile = import_path.default.join(traceFile, tracesDirMarker);
62
+ return toFilePathUrl(traceFile);
62
63
  } catch {
63
- throw new Error(`Trace file ${traceUrl} does not exist!`);
64
+ throw new Error(`Trace file ${traceFileOrUrl} does not exist!`);
64
65
  }
65
66
  }
66
67
  async function startTraceViewerServer(options) {
@@ -126,7 +127,7 @@ async function installRootRedirect(server, traceUrl, options) {
126
127
  });
127
128
  }
128
129
  async function runTraceViewerApp(traceUrl, browserName, options, exitOnClose) {
129
- traceUrl = validateTraceUrl(traceUrl);
130
+ traceUrl = validateTraceUrlOrPath(traceUrl);
130
131
  const server = await startTraceViewerServer(options);
131
132
  await installRootRedirect(server, traceUrl, options);
132
133
  const page = await openTraceViewerApp(server.urlPrefix("precise"), browserName, options);
@@ -135,7 +136,7 @@ async function runTraceViewerApp(traceUrl, browserName, options, exitOnClose) {
135
136
  return page;
136
137
  }
137
138
  async function runTraceInBrowser(traceUrl, options) {
138
- traceUrl = validateTraceUrl(traceUrl);
139
+ traceUrl = validateTraceUrlOrPath(traceUrl);
139
140
  const server = await startTraceViewerServer(options);
140
141
  await installRootRedirect(server, traceUrl, options);
141
142
  await openTraceInBrowser(server.urlPrefix("human-readable"));
@@ -177,8 +178,8 @@ async function openTraceInBrowser(url) {
177
178
  class StdinServer {
178
179
  constructor() {
179
180
  process.stdin.on("data", (data) => {
180
- const url = data.toString().trim();
181
- if (url === this._traceUrl)
181
+ const url = validateTraceUrlOrPath(data.toString().trim());
182
+ if (!url || url === this._traceUrl)
182
183
  return;
183
184
  if (url.endsWith(".json"))
184
185
  this._pollLoadTrace(url);
@@ -221,15 +222,18 @@ function traceDescriptor(traceDir, tracePrefix) {
221
222
  };
222
223
  for (const name of import_fs.default.readdirSync(traceDir)) {
223
224
  if (!tracePrefix || name.startsWith(tracePrefix))
224
- result.entries.push({ name, path: import_path.default.join(traceDir, name) });
225
+ result.entries.push({ name, path: toFilePathUrl(import_path.default.join(traceDir, name)) });
225
226
  }
226
227
  const resourcesDir = import_path.default.join(traceDir, "resources");
227
228
  if (import_fs.default.existsSync(resourcesDir)) {
228
229
  for (const name of import_fs.default.readdirSync(resourcesDir))
229
- result.entries.push({ name: "resources/" + name, path: import_path.default.join(resourcesDir, name) });
230
+ result.entries.push({ name: "resources/" + name, path: toFilePathUrl(import_path.default.join(resourcesDir, name)) });
230
231
  }
231
232
  return result;
232
233
  }
234
+ function toFilePathUrl(filePath) {
235
+ return `file?path=${encodeURIComponent(filePath)}`;
236
+ }
233
237
  // Annotate the CommonJS export names for ESM import in node:
234
238
  0 && (module.exports = {
235
239
  installRootRedirect,
@@ -18,10 +18,16 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var expectUtils_exports = {};
20
20
  __export(expectUtils_exports, {
21
- serializeExpectedTextValues: () => serializeExpectedTextValues
21
+ callLogText: () => callLogText,
22
+ formatMatcherMessage: () => formatMatcherMessage,
23
+ printReceivedStringContainExpectedResult: () => printReceivedStringContainExpectedResult,
24
+ printReceivedStringContainExpectedSubstring: () => printReceivedStringContainExpectedSubstring,
25
+ serializeExpectedTextValues: () => serializeExpectedTextValues,
26
+ simpleMatcherUtils: () => simpleMatcherUtils
22
27
  });
23
28
  module.exports = __toCommonJS(expectUtils_exports);
24
29
  var import_rtti = require("../../utils/isomorphic/rtti");
30
+ var import_utilsBundle = require("../../utilsBundle");
25
31
  function serializeExpectedTextValues(items, options = {}) {
26
32
  return items.map((i) => ({
27
33
  string: (0, import_rtti.isString)(i) ? i : void 0,
@@ -32,7 +38,86 @@ function serializeExpectedTextValues(items, options = {}) {
32
38
  normalizeWhiteSpace: options.normalizeWhiteSpace
33
39
  }));
34
40
  }
41
+ const printSubstring = (val) => val.replace(/"|\\/g, "\\$&");
42
+ const printReceivedStringContainExpectedSubstring = (utils, received, start, length) => utils.RECEIVED_COLOR(
43
+ '"' + printSubstring(received.slice(0, start)) + utils.INVERTED_COLOR(printSubstring(received.slice(start, start + length))) + printSubstring(received.slice(start + length)) + '"'
44
+ );
45
+ const printReceivedStringContainExpectedResult = (utils, received, result) => result === null ? utils.printReceived(received) : printReceivedStringContainExpectedSubstring(
46
+ utils,
47
+ received,
48
+ result.index,
49
+ result[0].length
50
+ );
51
+ function formatMatcherMessage(utils, details) {
52
+ const receiver = details.receiver ?? (details.locator ? "locator" : "page");
53
+ let message = utils.DIM_COLOR("expect(") + utils.RECEIVED_COLOR(receiver) + utils.DIM_COLOR(")" + (details.promise ? "." + details.promise : "") + (details.isNot ? ".not" : "") + ".") + details.matcherName + utils.DIM_COLOR("(") + utils.EXPECTED_COLOR(details.expectation) + utils.DIM_COLOR(")") + " failed\n\n";
54
+ const diffLines = details.printedDiff?.split("\n");
55
+ if (diffLines?.length === 2) {
56
+ details.printedExpected = diffLines[0];
57
+ details.printedReceived = diffLines[1];
58
+ details.printedDiff = void 0;
59
+ }
60
+ const align = !details.errorMessage && details.printedExpected?.startsWith("Expected:") && (!details.printedReceived || details.printedReceived.startsWith("Received:"));
61
+ if (details.locator)
62
+ message += `Locator: ${align ? " " : ""}${details.locator}
63
+ `;
64
+ if (details.printedExpected)
65
+ message += details.printedExpected + "\n";
66
+ if (details.printedReceived)
67
+ message += details.printedReceived + "\n";
68
+ if (details.timedOut && details.timeout)
69
+ message += `Timeout: ${align ? " " : ""}${details.timeout}ms
70
+ `;
71
+ if (details.printedDiff)
72
+ message += details.printedDiff + "\n";
73
+ if (details.errorMessage) {
74
+ message += details.errorMessage;
75
+ if (!details.errorMessage.endsWith("\n"))
76
+ message += "\n";
77
+ }
78
+ message += callLogText(utils, details.log);
79
+ return message;
80
+ }
81
+ const callLogText = (utils, log) => {
82
+ if (!log || !log.some((l) => !!l))
83
+ return "";
84
+ return `
85
+ Call log:
86
+ ${utils.DIM_COLOR(log.join("\n"))}
87
+ `;
88
+ };
89
+ function printValue(value) {
90
+ try {
91
+ return JSON.stringify(value);
92
+ } catch {
93
+ return String(value);
94
+ }
95
+ }
96
+ function printReceived(value) {
97
+ return import_utilsBundle.colors.red(printValue(value));
98
+ }
99
+ function printExpected(value) {
100
+ return import_utilsBundle.colors.green(printValue(value));
101
+ }
102
+ const simpleMatcherUtils = {
103
+ DIM_COLOR: import_utilsBundle.colors.dim,
104
+ RECEIVED_COLOR: import_utilsBundle.colors.red,
105
+ EXPECTED_COLOR: import_utilsBundle.colors.green,
106
+ INVERTED_COLOR: import_utilsBundle.colors.inverse,
107
+ printReceived,
108
+ printExpected,
109
+ printDiffOrStringify: (expected, received, expectedLabel, receivedLabel) => {
110
+ const maxLength = Math.max(expectedLabel.length, receivedLabel.length) + 2;
111
+ return `${expectedLabel}: `.padEnd(maxLength) + printExpected(expected) + `
112
+ ` + `${receivedLabel}: `.padEnd(maxLength) + printReceived(received);
113
+ }
114
+ };
35
115
  // Annotate the CommonJS export names for ESM import in node:
36
116
  0 && (module.exports = {
37
- serializeExpectedTextValues
117
+ callLogText,
118
+ formatMatcherMessage,
119
+ printReceivedStringContainExpectedResult,
120
+ printReceivedStringContainExpectedSubstring,
121
+ serializeExpectedTextValues,
122
+ simpleMatcherUtils
38
123
  });
@@ -28,11 +28,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var hostPlatform_exports = {};
30
30
  __export(hostPlatform_exports, {
31
+ hasGpuMac: () => hasGpuMac,
31
32
  hostPlatform: () => hostPlatform,
32
33
  isOfficiallySupportedPlatform: () => isOfficiallySupportedPlatform,
33
34
  shortPlatform: () => shortPlatform
34
35
  });
35
36
  module.exports = __toCommonJS(hostPlatform_exports);
37
+ var import_child_process = require("child_process");
36
38
  var import_os = __toESM(require("os"));
37
39
  var import_linuxUtils = require("./linuxUtils");
38
40
  function calculatePlatform() {
@@ -115,8 +117,21 @@ function toShortPlatform(hostPlatform2) {
115
117
  return hostPlatform2.endsWith("arm64") ? "linux-arm64" : "linux-x64";
116
118
  }
117
119
  const shortPlatform = toShortPlatform(hostPlatform);
120
+ let hasGpuMacValue;
121
+ function hasGpuMac() {
122
+ try {
123
+ if (hasGpuMacValue === void 0) {
124
+ const output = (0, import_child_process.execSync)("system_profiler SPDisplaysDataType", { stdio: ["ignore", "pipe", "ignore"] }).toString();
125
+ hasGpuMacValue = output.includes("Metal: Supported") || output.includes("Metal Support: Metal");
126
+ }
127
+ return hasGpuMacValue;
128
+ } catch (e) {
129
+ return false;
130
+ }
131
+ }
118
132
  // Annotate the CommonJS export names for ESM import in node:
119
133
  0 && (module.exports = {
134
+ hasGpuMac,
120
135
  hostPlatform,
121
136
  isOfficiallySupportedPlatform,
122
137
  shortPlatform
@@ -36,7 +36,6 @@ var import_path = __toESM(require("path"));
36
36
  var import_utilsBundle = require("../../utilsBundle");
37
37
  var import_crypto = require("./crypto");
38
38
  var import_assert = require("../../utils/isomorphic/assert");
39
- var import_manualPromise = require("../../utils/isomorphic/manualPromise");
40
39
  var import_network = require("./network");
41
40
  class HttpServer {
42
41
  constructor() {
@@ -59,20 +58,6 @@ class HttpServer {
59
58
  port() {
60
59
  return this._port;
61
60
  }
62
- async _tryStart(port, host) {
63
- const errorPromise = new import_manualPromise.ManualPromise();
64
- const errorListener = (error) => errorPromise.reject(error);
65
- this._server.on("error", errorListener);
66
- try {
67
- this._server.listen(port, host);
68
- await Promise.race([
69
- new Promise((cb) => this._server.once("listening", cb)),
70
- errorPromise
71
- ]);
72
- } finally {
73
- this._server.removeListener("error", errorListener);
74
- }
75
- }
76
61
  createWebSocket(transport, guid) {
77
62
  (0, import_assert.assert)(!this._wsGuid, "can only create one main websocket transport per server");
78
63
  this._wsGuid = guid || (0, import_crypto.createGuid)();
@@ -100,17 +85,17 @@ class HttpServer {
100
85
  async start(options = {}) {
101
86
  (0, import_assert.assert)(!this._started, "server already started");
102
87
  this._started = true;
103
- const host = options.host || "localhost";
88
+ const host = options.host;
104
89
  if (options.preferredPort) {
105
90
  try {
106
- await this._tryStart(options.preferredPort, host);
91
+ await (0, import_network.startHttpServer)(this._server, { port: options.preferredPort, host });
107
92
  } catch (e) {
108
93
  if (!e || !e.message || !e.message.includes("EADDRINUSE"))
109
94
  throw e;
110
- await this._tryStart(void 0, host);
95
+ await (0, import_network.startHttpServer)(this._server, { host });
111
96
  }
112
97
  } else {
113
- await this._tryStart(options.port, host);
98
+ await (0, import_network.startHttpServer)(this._server, { port: options.port, host });
114
99
  }
115
100
  const address = this._server.address();
116
101
  (0, import_assert.assert)(address, "Could not bind server socket");
@@ -121,7 +106,7 @@ class HttpServer {
121
106
  this._port = address.port;
122
107
  const resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
123
108
  this._urlPrefixPrecise = `http://${resolvedHost}:${address.port}`;
124
- this._urlPrefixHumanReadable = `http://${host}:${address.port}`;
109
+ this._urlPrefixHumanReadable = `http://${host ?? "localhost"}:${address.port}`;
125
110
  }
126
111
  }
127
112
  async stop() {
@@ -35,22 +35,20 @@ __export(network_exports, {
35
35
  createProxyAgent: () => createProxyAgent,
36
36
  fetchData: () => fetchData,
37
37
  httpRequest: () => httpRequest,
38
- isURLAvailable: () => isURLAvailable
38
+ isURLAvailable: () => isURLAvailable,
39
+ startHttpServer: () => startHttpServer
39
40
  });
40
41
  module.exports = __toCommonJS(network_exports);
41
42
  var import_http = __toESM(require("http"));
42
43
  var import_http2 = __toESM(require("http2"));
43
44
  var import_https = __toESM(require("https"));
44
- var import_url = __toESM(require("url"));
45
45
  var import_utilsBundle = require("../../utilsBundle");
46
46
  var import_happyEyeballs = require("./happyEyeballs");
47
47
  var import_manualPromise = require("../../utils/isomorphic/manualPromise");
48
48
  const NET_DEFAULT_TIMEOUT = 3e4;
49
49
  function httpRequest(params, onResponse, onError) {
50
- const parsedUrl = import_url.default.parse(params.url);
51
- let options = {
52
- ...parsedUrl,
53
- agent: parsedUrl.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent,
50
+ let url = new URL(params.url);
51
+ const options = {
54
52
  method: params.method || "GET",
55
53
  headers: params.headers
56
54
  };
@@ -58,21 +56,16 @@ function httpRequest(params, onResponse, onError) {
58
56
  options.rejectUnauthorized = params.rejectUnauthorized;
59
57
  const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
60
58
  if (proxyURL) {
59
+ const parsedProxyURL = normalizeProxyURL(proxyURL);
61
60
  if (params.url.startsWith("http:")) {
62
- const parsedProxyURL = import_url.default.parse(proxyURL);
63
- options = {
64
- path: parsedUrl.href,
65
- host: parsedProxyURL.hostname,
66
- port: parsedProxyURL.port,
67
- protocol: parsedProxyURL.protocol || "http:",
68
- headers: options.headers,
69
- method: options.method
70
- };
61
+ parsedProxyURL.pathname = url.toString();
62
+ url = parsedProxyURL;
71
63
  } else {
72
- options.agent = new import_utilsBundle.HttpsProxyAgent(normalizeProxyURL(proxyURL));
64
+ options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
73
65
  options.rejectUnauthorized = false;
74
66
  }
75
67
  }
68
+ options.agent ??= url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent;
76
69
  let cancelRequest;
77
70
  const requestCallback = (res) => {
78
71
  const statusCode = res.statusCode || 0;
@@ -83,7 +76,7 @@ function httpRequest(params, onResponse, onError) {
83
76
  onResponse(res);
84
77
  }
85
78
  };
86
- const request = options.protocol === "https:" ? import_https.default.request(options, requestCallback) : import_http.default.request(options, requestCallback);
79
+ const request = url.protocol === "https:" ? import_https.default.request(url, options, requestCallback) : import_http.default.request(url, options, requestCallback);
87
80
  request.on("error", onError);
88
81
  if (params.socketTimeout !== void 0) {
89
82
  request.setTimeout(params.socketTimeout, () => {
@@ -122,7 +115,7 @@ async function fetchData(progress, params, onError) {
122
115
  throw error;
123
116
  }
124
117
  }
125
- function shouldBypassProxy(url2, bypass) {
118
+ function shouldBypassProxy(url, bypass) {
126
119
  if (!bypass)
127
120
  return false;
128
121
  const domains = bypass.split(",").map((s) => {
@@ -131,7 +124,7 @@ function shouldBypassProxy(url2, bypass) {
131
124
  s = "." + s;
132
125
  return s;
133
126
  });
134
- const domain = "." + url2.hostname;
127
+ const domain = "." + url.hostname;
135
128
  return domains.some((d) => domain.endsWith(d));
136
129
  }
137
130
  function normalizeProxyURL(proxy) {
@@ -177,20 +170,35 @@ function createHttp2Server(...args) {
177
170
  decorateServer(server);
178
171
  return server;
179
172
  }
180
- async function isURLAvailable(url2, ignoreHTTPSErrors, onLog, onStdErr) {
181
- let statusCode = await httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr);
182
- if (statusCode === 404 && url2.pathname === "/") {
183
- const indexUrl = new URL(url2);
173
+ async function startHttpServer(server, options) {
174
+ const { host = "localhost", port = 0 } = options;
175
+ const errorPromise = new import_manualPromise.ManualPromise();
176
+ const errorListener = (error) => errorPromise.reject(error);
177
+ server.on("error", errorListener);
178
+ try {
179
+ server.listen(port, host);
180
+ await Promise.race([
181
+ new Promise((cb) => server.once("listening", cb)),
182
+ errorPromise
183
+ ]);
184
+ } finally {
185
+ server.removeListener("error", errorListener);
186
+ }
187
+ }
188
+ async function isURLAvailable(url, ignoreHTTPSErrors, onLog, onStdErr) {
189
+ let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
190
+ if (statusCode === 404 && url.pathname === "/") {
191
+ const indexUrl = new URL(url);
184
192
  indexUrl.pathname = "/index.html";
185
193
  statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
186
194
  }
187
195
  return statusCode >= 200 && statusCode < 404;
188
196
  }
189
- async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
197
+ async function httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr) {
190
198
  return new Promise((resolve) => {
191
- onLog?.(`HTTP GET: ${url2}`);
199
+ onLog?.(`HTTP GET: ${url}`);
192
200
  httpRequest({
193
- url: url2.toString(),
201
+ url: url.toString(),
194
202
  headers: { Accept: "*/*" },
195
203
  rejectUnauthorized: !ignoreHTTPSErrors
196
204
  }, (res) => {
@@ -201,7 +209,7 @@ async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
201
209
  }, (error) => {
202
210
  if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
203
211
  onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
204
- onLog?.(`Error while checking if ${url2} is available: ${error.message}`);
212
+ onLog?.(`Error while checking if ${url} is available: ${error.message}`);
205
213
  resolve(0);
206
214
  });
207
215
  });
@@ -229,5 +237,6 @@ function decorateServer(server) {
229
237
  createProxyAgent,
230
238
  fetchData,
231
239
  httpRequest,
232
- isURLAvailable
240
+ isURLAvailable,
241
+ startHttpServer
233
242
  });
@@ -42,6 +42,7 @@ var import_utilsBundle = require("../../utilsBundle");
42
42
  var import_debugLogger = require("./debugLogger");
43
43
  var import_zones = require("./zones");
44
44
  var import_debug = require("./debug");
45
+ var import_mcpBundle = require("../../mcpBundle");
45
46
  const pipelineAsync = util.promisify(import_stream.pipeline);
46
47
  class NodeZone {
47
48
  constructor(zone) {
@@ -105,6 +106,11 @@ const nodePlatform = {
105
106
  streamWritable: (channel) => {
106
107
  return new WritableStreamImpl(channel);
107
108
  },
109
+ zodToJsonSchema: (schema) => {
110
+ if ("_zod" in schema)
111
+ return import_mcpBundle.z.toJSONSchema(schema);
112
+ return (0, import_mcpBundle.zodToJsonSchema)(schema);
113
+ },
108
114
  zones: {
109
115
  current: () => new NodeZone((0, import_zones.currentZone)()),
110
116
  empty: new NodeZone(import_zones.emptyZone)
@@ -21,12 +21,11 @@ __export(videoRecorder_exports, {
21
21
  VideoRecorder: () => VideoRecorder
22
22
  });
23
23
  module.exports = __toCommonJS(videoRecorder_exports);
24
- var import_utils = require("../../utils");
25
- var import_page = require("../page");
26
- var import_processLauncher = require("../utils/processLauncher");
24
+ var import_utils = require("../utils");
25
+ var import_processLauncher = require("./utils/processLauncher");
27
26
  const fps = 25;
28
27
  class VideoRecorder {
29
- constructor(page, ffmpegPath) {
28
+ constructor(ffmpegPath, options) {
30
29
  this._process = null;
31
30
  this._gracefullyClose = null;
32
31
  this._lastWritePromise = Promise.resolve();
@@ -36,16 +35,12 @@ class VideoRecorder {
36
35
  this._frameQueue = [];
37
36
  this._isStopped = false;
38
37
  this._ffmpegPath = ffmpegPath;
39
- page.on(import_page.Page.Events.ScreencastFrame, (frame) => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
40
- }
41
- static async launch(page, ffmpegPath, options) {
42
38
  if (!options.outputFile.endsWith(".webm"))
43
39
  throw new Error("File must have .webm extension");
44
- const recorder = new VideoRecorder(page, ffmpegPath);
45
- await recorder._launch(options);
46
- return recorder;
40
+ this._launchPromise = this._launch(options).catch((e) => e);
47
41
  }
48
42
  async _launch(options) {
43
+ await (0, import_utils.mkdirIfNeeded)(options.outputFile);
49
44
  const w = options.width;
50
45
  const h = options.height;
51
46
  const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
@@ -74,6 +69,13 @@ class VideoRecorder {
74
69
  this._gracefullyClose = gracefullyClose;
75
70
  }
76
71
  writeFrame(frame, timestamp) {
72
+ this._launchPromise.then((error) => {
73
+ if (error)
74
+ return;
75
+ this._writeFrame(frame, timestamp);
76
+ });
77
+ }
78
+ _writeFrame(frame, timestamp) {
77
79
  (0, import_utils.assert)(this._process);
78
80
  if (this._isStopped)
79
81
  return;
@@ -100,13 +102,20 @@ class VideoRecorder {
100
102
  });
101
103
  }
102
104
  async stop() {
105
+ const error = await this._launchPromise;
106
+ if (error)
107
+ throw error;
103
108
  if (this._isStopped || !this._lastFrame)
104
109
  return;
105
110
  const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
106
- this.writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
111
+ this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
107
112
  this._isStopped = true;
108
- await this._lastWritePromise;
109
- await this._gracefullyClose();
113
+ try {
114
+ await this._lastWritePromise;
115
+ await this._gracefullyClose();
116
+ } catch (e) {
117
+ import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
118
+ }
110
119
  }
111
120
  }
112
121
  // Annotate the CommonJS export names for ESM import in node:
@@ -51,12 +51,10 @@ class WebKit extends import_browserType.BrowserType {
51
51
  CURL_COOKIE_JAR_PATH: process.platform === "win32" && isPersistent ? import_path.default.join(userDataDir, "cookiejar.db") : void 0
52
52
  };
53
53
  }
54
- doRewriteStartupLog(error) {
55
- if (!error.logs)
56
- return error;
57
- if (error.logs.includes("Failed to open display") || error.logs.includes("cannot open display"))
58
- error.logs = "\n" + (0, import_ascii.wrapInASCIIBox)(import_browserType.kNoXServerRunningError, 1);
59
- return error;
54
+ doRewriteStartupLog(logs) {
55
+ if (logs.includes("Failed to open display") || logs.includes("cannot open display"))
56
+ logs = "\n" + (0, import_ascii.wrapInASCIIBox)(import_browserType.kNoXServerRunningError, 1);
57
+ return logs;
60
58
  }
61
59
  attemptToGracefullyCloseBrowser(transport) {
62
60
  transport.send({ method: "Playwright.close", params: {}, id: import_wkConnection.kBrowserCloseMessageId });
@@ -56,7 +56,6 @@ class WKBrowser extends import_browser.Browser {
56
56
  this._browserSession.on("Playwright.downloadCreated", this._onDownloadCreated.bind(this));
57
57
  this._browserSession.on("Playwright.downloadFilenameSuggested", this._onDownloadFilenameSuggested.bind(this));
58
58
  this._browserSession.on("Playwright.downloadFinished", this._onDownloadFinished.bind(this));
59
- this._browserSession.on("Playwright.screencastFinished", this._onScreencastFinished.bind(this));
60
59
  this._browserSession.on(import_wkConnection.kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this));
61
60
  }
62
61
  static async connect(parent, transport, options) {
@@ -79,7 +78,7 @@ class WKBrowser extends import_browser.Browser {
79
78
  wkPage.didClose();
80
79
  this._wkPages.clear();
81
80
  for (const video of this._idToVideo.values())
82
- video.artifact.reportFinished(new import_errors.TargetClosedError());
81
+ video.artifact.reportFinished(new import_errors.TargetClosedError(this.closeReason()));
83
82
  this._idToVideo.clear();
84
83
  this._didClose();
85
84
  }
@@ -131,9 +130,6 @@ class WKBrowser extends import_browser.Browser {
131
130
  _onDownloadFinished(payload) {
132
131
  this._downloadFinished(payload.uuid, payload.error);
133
132
  }
134
- _onScreencastFinished(payload) {
135
- this._takeVideo(payload.screencastId)?.reportFinished();
136
- }
137
133
  _onPageProxyCreated(event) {
138
134
  const pageProxyId = event.pageProxyId;
139
135
  let context = null;
@@ -315,7 +311,7 @@ class WKBrowserContext extends import_browserContext.BrowserContext {
315
311
  }
316
312
  async doClose(reason) {
317
313
  if (!this._browserContextId) {
318
- await Promise.all(this._wkPages().map((wkPage) => wkPage._stopVideo()));
314
+ await Promise.all(this._wkPages().map((wkPage) => wkPage._page.screencast.stopVideoRecording()));
319
315
  await this._browser.close({ reason });
320
316
  } else {
321
317
  await this._browser._browserSession.send("Playwright.deleteContext", { browserContextId: this._browserContextId });
@@ -89,11 +89,6 @@ class WKSession extends import_events.EventEmitter {
89
89
  this.connection = connection;
90
90
  this.sessionId = sessionId;
91
91
  this._rawSend = rawSend;
92
- this.on = super.on;
93
- this.off = super.removeListener;
94
- this.addListener = super.addListener;
95
- this.removeListener = super.removeListener;
96
- this.once = super.once;
97
92
  }
98
93
  async send(method, params) {
99
94
  if (this._crashed || this._disposed || this.connection._browserDisconnectedLogs)
@@ -134,7 +129,7 @@ class WKSession extends import_events.EventEmitter {
134
129
  callback.resolve(object.result);
135
130
  }
136
131
  } else if (object.id && !object.error) {
137
- (0, import_utils.assert)(this.isDisposed());
132
+ (0, import_utils.assert)(this.isDisposed(), JSON.stringify(object));
138
133
  } else {
139
134
  Promise.resolve().then(() => this.emit(object.method, object.params));
140
135
  }
@@ -54,7 +54,7 @@ class WKInterceptableRequest {
54
54
  constructor(session, frame, event, redirectedFrom, documentId) {
55
55
  this._session = session;
56
56
  this._requestId = event.requestId;
57
- const resourceType = event.type ? event.type.toLowerCase() : redirectedFrom ? redirectedFrom.request.resourceType() : "other";
57
+ const resourceType = event.type ? toResourceType(event.type) : redirectedFrom ? redirectedFrom.request.resourceType() : "other";
58
58
  let postDataBuffer = null;
59
59
  this._timestamp = event.timestamp;
60
60
  this._wallTime = event.walltime * 1e3;
@@ -162,6 +162,34 @@ function wkMillisToRoundishMillis(value) {
162
162
  }
163
163
  return (value * 1e3 | 0) / 1e3;
164
164
  }
165
+ function toResourceType(type) {
166
+ switch (type) {
167
+ case "Document":
168
+ return "document";
169
+ case "StyleSheet":
170
+ return "stylesheet";
171
+ case "Image":
172
+ return "image";
173
+ case "Font":
174
+ return "font";
175
+ case "Script":
176
+ return "script";
177
+ case "XHR":
178
+ return "xhr";
179
+ case "Fetch":
180
+ return "fetch";
181
+ case "Ping":
182
+ return "ping";
183
+ case "Beacon":
184
+ return "beacon";
185
+ case "WebSocket":
186
+ return "websocket";
187
+ case "EventSource":
188
+ return "eventsource";
189
+ default:
190
+ return "other";
191
+ }
192
+ }
165
193
  // Annotate the CommonJS export names for ESM import in node:
166
194
  0 && (module.exports = {
167
195
  WKInterceptableRequest,