patchright-core 1.57.0 → 1.59.0

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 (307) hide show
  1. package/ThirdPartyNotices.txt +2688 -297
  2. package/browsers.json +23 -22
  3. package/lib/bootstrap.js +77 -0
  4. package/lib/cli/browserActions.js +308 -0
  5. package/lib/cli/driver.js +3 -2
  6. package/lib/cli/installActions.js +171 -0
  7. package/lib/cli/program.js +48 -413
  8. package/lib/client/android.js +4 -4
  9. package/lib/client/api.js +3 -0
  10. package/lib/client/browser.js +11 -5
  11. package/lib/client/browserContext.js +20 -23
  12. package/lib/client/browserType.js +23 -54
  13. package/lib/client/cdpSession.js +6 -2
  14. package/lib/client/channelOwner.js +1 -1
  15. package/lib/client/clientHelper.js +2 -1
  16. package/lib/client/clock.js +0 -1
  17. package/lib/client/{webSocket.js → connect.js} +57 -7
  18. package/lib/client/connection.js +8 -0
  19. package/lib/client/consoleMessage.js +3 -0
  20. package/lib/client/debugger.js +57 -0
  21. package/lib/client/dialog.js +8 -1
  22. package/lib/client/disposable.js +76 -0
  23. package/lib/client/electron.js +1 -0
  24. package/lib/client/elementHandle.js +1 -1
  25. package/lib/client/events.js +3 -0
  26. package/lib/client/fetch.js +2 -4
  27. package/lib/client/frame.js +9 -13
  28. package/lib/client/harRouter.js +13 -1
  29. package/lib/client/jsHandle.js +4 -8
  30. package/lib/client/locator.js +13 -36
  31. package/lib/client/network.js +14 -11
  32. package/lib/client/page.js +44 -50
  33. package/lib/client/screencast.js +88 -0
  34. package/lib/client/selectors.js +3 -1
  35. package/lib/client/tracing.js +11 -5
  36. package/lib/client/video.js +13 -20
  37. package/lib/client/worker.js +6 -6
  38. package/lib/generated/bindingsControllerSource.js +1 -1
  39. package/lib/generated/clockSource.js +1 -1
  40. package/lib/generated/injectedScriptSource.js +1 -1
  41. package/lib/generated/pollingRecorderSource.js +1 -1
  42. package/lib/generated/storageScriptSource.js +1 -1
  43. package/lib/generated/utilityScriptSource.js +1 -1
  44. package/lib/mcpBundle.js +78 -0
  45. package/lib/mcpBundleImpl.js +91 -0
  46. package/lib/protocol/serializers.js +5 -0
  47. package/lib/protocol/validator.js +228 -58
  48. package/lib/protocol/validatorPrimitives.js +1 -1
  49. package/lib/remote/playwrightConnection.js +10 -8
  50. package/lib/remote/playwrightPipeServer.js +100 -0
  51. package/lib/remote/playwrightServer.js +14 -10
  52. package/lib/remote/playwrightWebSocketServer.js +73 -0
  53. package/lib/remote/serverTransport.js +96 -0
  54. package/lib/server/android/android.js +2 -2
  55. package/lib/server/artifact.js +1 -1
  56. package/lib/server/bidi/bidiBrowser.js +80 -14
  57. package/lib/server/bidi/bidiChromium.js +23 -14
  58. package/lib/server/bidi/bidiConnection.js +1 -0
  59. package/lib/server/bidi/bidiDeserializer.js +116 -0
  60. package/lib/server/bidi/bidiExecutionContext.js +75 -29
  61. package/lib/server/bidi/bidiFirefox.js +6 -8
  62. package/lib/server/bidi/bidiNetworkManager.js +40 -12
  63. package/lib/server/bidi/bidiPage.js +67 -40
  64. package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
  65. package/lib/server/bidi/third_party/firefoxPrefs.js +3 -1
  66. package/lib/server/browser.js +84 -21
  67. package/lib/server/browserContext.js +137 -77
  68. package/lib/server/browserType.js +26 -16
  69. package/lib/server/chromium/chromium.js +28 -31
  70. package/lib/server/chromium/chromiumSwitches.js +16 -4
  71. package/lib/server/chromium/crBrowser.js +40 -27
  72. package/lib/server/chromium/crConnection.js +0 -5
  73. package/lib/server/chromium/crDevTools.js +1 -2
  74. package/lib/server/chromium/crNetworkManager.js +54 -229
  75. package/lib/server/chromium/crPage.js +74 -260
  76. package/lib/server/chromium/crServiceWorker.js +7 -14
  77. package/lib/server/clock.js +33 -41
  78. package/lib/server/codegen/javascript.js +6 -29
  79. package/lib/server/console.js +5 -1
  80. package/lib/server/debugController.js +12 -6
  81. package/lib/server/debugger.js +40 -47
  82. package/lib/server/deviceDescriptorsSource.json +137 -137
  83. package/lib/server/dispatchers/browserContextDispatcher.js +30 -30
  84. package/lib/server/dispatchers/browserDispatcher.js +11 -5
  85. package/lib/server/dispatchers/browserTypeDispatcher.js +7 -0
  86. package/lib/server/dispatchers/cdpSessionDispatcher.js +4 -1
  87. package/lib/server/dispatchers/debuggerDispatcher.js +84 -0
  88. package/lib/server/dispatchers/dispatcher.js +7 -14
  89. package/lib/server/dispatchers/disposableDispatcher.js +39 -0
  90. package/lib/server/dispatchers/electronDispatcher.js +2 -1
  91. package/lib/server/dispatchers/frameDispatcher.js +7 -7
  92. package/lib/server/dispatchers/localUtilsDispatcher.js +37 -1
  93. package/lib/server/dispatchers/networkDispatchers.js +6 -5
  94. package/lib/server/dispatchers/pageDispatcher.js +101 -34
  95. package/lib/server/dispatchers/webSocketRouteDispatcher.js +4 -5
  96. package/lib/server/disposable.js +41 -0
  97. package/lib/server/dom.js +56 -29
  98. package/lib/server/download.js +3 -2
  99. package/lib/server/electron/electron.js +17 -9
  100. package/lib/server/firefox/ffBrowser.js +9 -29
  101. package/lib/server/firefox/ffConnection.js +0 -5
  102. package/lib/server/firefox/ffInput.js +21 -5
  103. package/lib/server/firefox/ffNetworkManager.js +4 -4
  104. package/lib/server/firefox/ffPage.js +27 -33
  105. package/lib/server/firefox/firefox.js +6 -8
  106. package/lib/server/frameSelectors.js +14 -169
  107. package/lib/server/frames.js +263 -551
  108. package/lib/server/har/harRecorder.js +2 -2
  109. package/lib/server/har/harTracer.js +5 -4
  110. package/lib/server/input.js +49 -4
  111. package/lib/server/instrumentation.js +8 -0
  112. package/lib/server/javascript.js +6 -22
  113. package/lib/server/launchApp.js +0 -1
  114. package/lib/server/localUtils.js +6 -6
  115. package/lib/server/network.js +59 -20
  116. package/lib/server/overlay.js +138 -0
  117. package/lib/server/page.js +179 -157
  118. package/lib/server/progress.js +32 -6
  119. package/lib/server/recorder/recorderApp.js +84 -104
  120. package/lib/server/recorder.js +76 -40
  121. package/lib/server/registry/browserFetcher.js +6 -4
  122. package/lib/server/registry/index.js +222 -226
  123. package/lib/server/registry/nativeDeps.js +1 -0
  124. package/lib/server/registry/oopDownloadBrowserMain.js +3 -0
  125. package/lib/server/screencast.js +137 -0
  126. package/lib/server/trace/recorder/snapshotter.js +2 -2
  127. package/lib/server/trace/recorder/snapshotterInjected.js +21 -1
  128. package/lib/server/trace/recorder/tracing.js +98 -47
  129. package/lib/server/trace/viewer/traceViewer.js +24 -21
  130. package/lib/server/usKeyboardLayout.js +7 -0
  131. package/lib/server/utils/comparators.js +1 -1
  132. package/lib/server/utils/disposable.js +32 -0
  133. package/lib/server/utils/eventsHelper.js +3 -1
  134. package/lib/server/utils/expectUtils.js +87 -2
  135. package/lib/server/utils/fileUtils.js +16 -2
  136. package/lib/server/utils/happyEyeballs.js +15 -12
  137. package/lib/server/utils/httpServer.js +10 -23
  138. package/lib/server/utils/network.js +39 -29
  139. package/lib/server/utils/processLauncher.js +8 -6
  140. package/lib/server/utils/zipFile.js +2 -2
  141. package/lib/server/videoRecorder.js +194 -0
  142. package/lib/server/webkit/webkit.js +4 -6
  143. package/lib/server/webkit/wkBrowser.js +1 -10
  144. package/lib/server/webkit/wkConnection.js +1 -6
  145. package/lib/server/webkit/wkInterceptableRequest.js +29 -1
  146. package/lib/server/webkit/wkPage.js +88 -57
  147. package/lib/server/webkit/wkWorkers.js +2 -1
  148. package/lib/serverRegistry.js +156 -0
  149. package/lib/tools/backend/browserBackend.js +79 -0
  150. package/lib/tools/backend/common.js +63 -0
  151. package/lib/tools/backend/config.js +41 -0
  152. package/lib/tools/backend/console.js +66 -0
  153. package/lib/tools/backend/context.js +296 -0
  154. package/lib/tools/backend/cookies.js +152 -0
  155. package/lib/tools/backend/devtools.js +69 -0
  156. package/lib/tools/backend/dialogs.js +59 -0
  157. package/lib/tools/backend/evaluate.js +64 -0
  158. package/lib/tools/backend/files.js +60 -0
  159. package/lib/tools/backend/form.js +64 -0
  160. package/lib/tools/backend/keyboard.js +155 -0
  161. package/lib/tools/backend/logFile.js +95 -0
  162. package/lib/tools/backend/mouse.js +168 -0
  163. package/lib/tools/backend/navigate.js +106 -0
  164. package/lib/tools/backend/network.js +135 -0
  165. package/lib/tools/backend/pdf.js +48 -0
  166. package/lib/tools/backend/response.js +305 -0
  167. package/lib/tools/backend/route.js +140 -0
  168. package/lib/tools/backend/runCode.js +77 -0
  169. package/lib/tools/backend/screenshot.js +88 -0
  170. package/lib/tools/backend/sessionLog.js +74 -0
  171. package/lib/tools/backend/snapshot.js +208 -0
  172. package/lib/tools/backend/storage.js +68 -0
  173. package/lib/tools/backend/tab.js +445 -0
  174. package/lib/tools/backend/tabs.js +67 -0
  175. package/lib/tools/backend/tool.js +47 -0
  176. package/lib/tools/backend/tools.js +102 -0
  177. package/lib/tools/backend/tracing.js +78 -0
  178. package/lib/tools/backend/utils.js +83 -0
  179. package/lib/tools/backend/verify.js +151 -0
  180. package/lib/tools/backend/video.js +98 -0
  181. package/lib/tools/backend/wait.js +63 -0
  182. package/lib/tools/backend/webstorage.js +223 -0
  183. package/lib/tools/cli-client/cli.js +6 -0
  184. package/lib/tools/cli-client/help.json +399 -0
  185. package/lib/tools/cli-client/minimist.js +128 -0
  186. package/lib/tools/cli-client/program.js +350 -0
  187. package/lib/tools/cli-client/registry.js +176 -0
  188. package/lib/tools/cli-client/session.js +289 -0
  189. package/lib/tools/cli-client/skill/SKILL.md +328 -0
  190. package/lib/tools/cli-client/skill/references/element-attributes.md +23 -0
  191. package/lib/tools/cli-client/skill/references/playwright-tests.md +39 -0
  192. package/lib/tools/cli-client/skill/references/request-mocking.md +87 -0
  193. package/lib/tools/cli-client/skill/references/running-code.md +231 -0
  194. package/lib/tools/cli-client/skill/references/session-management.md +169 -0
  195. package/lib/tools/cli-client/skill/references/storage-state.md +275 -0
  196. package/lib/tools/cli-client/skill/references/test-generation.md +88 -0
  197. package/lib/tools/cli-client/skill/references/tracing.md +139 -0
  198. package/lib/tools/cli-client/skill/references/video-recording.md +143 -0
  199. package/lib/tools/cli-daemon/command.js +73 -0
  200. package/lib/tools/cli-daemon/commands.js +956 -0
  201. package/lib/tools/cli-daemon/daemon.js +157 -0
  202. package/lib/tools/cli-daemon/helpGenerator.js +177 -0
  203. package/lib/tools/cli-daemon/program.js +129 -0
  204. package/lib/tools/dashboard/appIcon.png +0 -0
  205. package/lib/tools/dashboard/dashboardApp.js +284 -0
  206. package/lib/tools/dashboard/dashboardController.js +296 -0
  207. package/lib/tools/exports.js +60 -0
  208. package/lib/tools/mcp/browserFactory.js +233 -0
  209. package/lib/tools/mcp/cdpRelay.js +352 -0
  210. package/lib/tools/mcp/cli-stub.js +7 -0
  211. package/lib/tools/mcp/config.d.js +16 -0
  212. package/lib/tools/mcp/config.js +446 -0
  213. package/lib/tools/mcp/configIni.js +189 -0
  214. package/lib/tools/mcp/extensionContextFactory.js +55 -0
  215. package/lib/tools/mcp/index.js +62 -0
  216. package/lib/tools/mcp/log.js +35 -0
  217. package/lib/tools/mcp/program.js +107 -0
  218. package/lib/tools/mcp/protocol.js +28 -0
  219. package/lib/tools/mcp/watchdog.js +44 -0
  220. package/lib/tools/trace/SKILL.md +171 -0
  221. package/lib/tools/trace/installSkill.js +48 -0
  222. package/lib/tools/trace/traceActions.js +142 -0
  223. package/lib/tools/trace/traceAttachments.js +69 -0
  224. package/lib/tools/trace/traceCli.js +87 -0
  225. package/lib/tools/trace/traceConsole.js +97 -0
  226. package/lib/tools/trace/traceErrors.js +55 -0
  227. package/lib/tools/trace/traceOpen.js +69 -0
  228. package/lib/tools/trace/traceParser.js +96 -0
  229. package/lib/tools/trace/traceRequests.js +182 -0
  230. package/lib/tools/trace/traceScreenshot.js +68 -0
  231. package/lib/tools/trace/traceSnapshot.js +149 -0
  232. package/lib/tools/trace/traceUtils.js +153 -0
  233. package/lib/tools/utils/connect.js +32 -0
  234. package/lib/tools/utils/mcp/http.js +152 -0
  235. package/lib/tools/utils/mcp/server.js +230 -0
  236. package/lib/tools/utils/mcp/tool.js +47 -0
  237. package/lib/tools/utils/socketConnection.js +108 -0
  238. package/lib/utils/isomorphic/ariaSnapshot.js +60 -2
  239. package/lib/utils/isomorphic/formatUtils.js +64 -0
  240. package/lib/utils/isomorphic/jsonSchema.js +89 -0
  241. package/lib/utils/isomorphic/lruCache.js +51 -0
  242. package/lib/utils/isomorphic/mimeType.js +7 -2
  243. package/lib/utils/isomorphic/protocolFormatter.js +2 -2
  244. package/lib/utils/isomorphic/protocolMetainfo.js +127 -98
  245. package/lib/utils/isomorphic/stringUtils.js +49 -0
  246. package/lib/utils/isomorphic/timeoutRunner.js +3 -3
  247. package/lib/utils/isomorphic/trace/entries.js +16 -0
  248. package/lib/utils/isomorphic/trace/snapshotRenderer.js +492 -0
  249. package/lib/utils/isomorphic/trace/snapshotServer.js +120 -0
  250. package/lib/utils/isomorphic/trace/snapshotStorage.js +89 -0
  251. package/lib/utils/isomorphic/trace/traceLoader.js +132 -0
  252. package/lib/utils/isomorphic/trace/traceModel.js +366 -0
  253. package/lib/utils/isomorphic/trace/traceModernizer.js +401 -0
  254. package/lib/utils/isomorphic/trace/versions/traceV3.js +16 -0
  255. package/lib/utils/isomorphic/trace/versions/traceV4.js +16 -0
  256. package/lib/utils/isomorphic/trace/versions/traceV5.js +16 -0
  257. package/lib/utils/isomorphic/trace/versions/traceV6.js +16 -0
  258. package/lib/utils/isomorphic/trace/versions/traceV7.js +16 -0
  259. package/lib/utils/isomorphic/trace/versions/traceV8.js +16 -0
  260. package/lib/utils/isomorphic/urlMatch.js +54 -1
  261. package/lib/utils/isomorphic/utilityScriptSerializers.js +11 -0
  262. package/lib/utils/isomorphic/yaml.js +84 -0
  263. package/lib/utils.js +8 -2
  264. package/lib/utilsBundle.js +5 -26
  265. package/lib/utilsBundleImpl/index.js +172 -173
  266. package/lib/vite/dashboard/assets/index-BAOybkp8.js +50 -0
  267. package/lib/vite/dashboard/assets/index-CZAYOG76.css +1 -0
  268. package/lib/vite/dashboard/index.html +28 -0
  269. package/lib/vite/htmlReport/index.html +2 -70
  270. package/lib/vite/htmlReport/report.css +1 -0
  271. package/lib/vite/htmlReport/report.js +72 -0
  272. package/lib/vite/recorder/assets/codeMirrorModule-C8KMvO9L.js +32 -0
  273. package/lib/vite/recorder/assets/{codeMirrorModule-C3UTv-Ge.css → codeMirrorModule-DYBRYzYX.css} +1 -1
  274. package/lib/vite/recorder/assets/{index-Ri0uHF7I.css → index-BSjZa4pk.css} +1 -1
  275. package/lib/vite/recorder/assets/index-CqAYX1I3.js +193 -0
  276. package/lib/vite/recorder/index.html +2 -2
  277. package/lib/vite/traceViewer/assets/codeMirrorModule-DS0FLvoc.js +32 -0
  278. package/lib/vite/traceViewer/assets/defaultSettingsView-GTWI-W_B.js +262 -0
  279. package/lib/vite/traceViewer/{codeMirrorModule.C3UTv-Ge.css → codeMirrorModule.DYBRYzYX.css} +1 -1
  280. package/lib/vite/traceViewer/defaultSettingsView.B4dS75f0.css +1 -0
  281. package/lib/vite/traceViewer/index.CzXZzn5A.css +1 -0
  282. package/lib/vite/traceViewer/index.Dtstcb7U.js +2 -0
  283. package/lib/vite/traceViewer/index.html +4 -4
  284. package/lib/vite/traceViewer/sw.bundle.js +5 -3
  285. package/lib/vite/traceViewer/uiMode.Vipi55dB.js +6 -0
  286. package/lib/vite/traceViewer/uiMode.html +3 -3
  287. package/lib/zipBundleImpl.js +2 -2
  288. package/lib/zodBundle.js +39 -0
  289. package/lib/zodBundleImpl.js +40 -0
  290. package/package.json +7 -1
  291. package/types/protocol.d.ts +1696 -221
  292. package/types/types.d.ts +879 -112
  293. package/lib/server/bidi/third_party/bidiDeserializer.js +0 -98
  294. package/lib/server/chromium/videoRecorder.js +0 -115
  295. package/lib/server/pageBinding.js +0 -87
  296. package/lib/server/trace/test/inMemorySnapshotter.js +0 -87
  297. package/lib/utils/isomorphic/oldUtilityScriptSerializers.js +0 -248
  298. package/lib/vite/recorder/assets/codeMirrorModule-CBbSe-ZI.js +0 -25
  299. package/lib/vite/recorder/assets/index-CpZVd2nA.js +0 -193
  300. package/lib/vite/traceViewer/assets/codeMirrorModule-DHz0wP2C.js +0 -25
  301. package/lib/vite/traceViewer/assets/defaultSettingsView-WsZP88O6.js +0 -266
  302. package/lib/vite/traceViewer/defaultSettingsView.ConWv5KN.css +0 -1
  303. package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
  304. package/lib/vite/traceViewer/index.C8xAeo93.js +0 -2
  305. package/lib/vite/traceViewer/uiMode.BltraIJB.js +0 -5
  306. /package/lib/{server/utils → utils/isomorphic}/imageUtils.js +0 -0
  307. /package/lib/utils/isomorphic/{traceUtils.js → trace/traceUtils.js} +0 -0
@@ -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
  });
@@ -32,6 +32,7 @@ __export(fileUtils_exports, {
32
32
  canAccessFile: () => canAccessFile,
33
33
  copyFileAndMakeWritable: () => copyFileAndMakeWritable,
34
34
  existsAsync: () => existsAsync,
35
+ makeSocketPath: () => makeSocketPath,
35
36
  mkdirIfNeeded: () => mkdirIfNeeded,
36
37
  removeFolders: () => removeFolders,
37
38
  sanitizeForFilePath: () => sanitizeForFilePath,
@@ -39,9 +40,10 @@ __export(fileUtils_exports, {
39
40
  });
40
41
  module.exports = __toCommonJS(fileUtils_exports);
41
42
  var import_fs = __toESM(require("fs"));
43
+ var import_os = __toESM(require("os"));
42
44
  var import_path = __toESM(require("path"));
45
+ var import_crypto = require("./crypto");
43
46
  var import_manualPromise = require("../../utils/isomorphic/manualPromise");
44
- var import_zipBundle = require("../../zipBundle");
45
47
  const existsAsync = (path2) => new Promise((resolve) => import_fs.default.stat(path2, (err) => resolve(!err)));
46
48
  async function mkdirIfNeeded(filePath) {
47
49
  await import_fs.default.promises.mkdir(import_path.default.dirname(filePath), { recursive: true }).catch(() => {
@@ -72,6 +74,16 @@ function sanitizeForFilePath(s) {
72
74
  function toPosixPath(aPath) {
73
75
  return aPath.split(import_path.default.sep).join(import_path.default.posix.sep);
74
76
  }
77
+ function makeSocketPath(domain, name) {
78
+ const userNameHash = (0, import_crypto.calculateSha1)(process.env.USERNAME || process.env.USER || "default").slice(0, 8);
79
+ if (process.platform === "win32")
80
+ return `\\\\.\\pipe\\pw-${userNameHash}-${domain}-${name}`;
81
+ const baseDir = process.env.PLAYWRIGHT_SOCKETS_DIR || import_path.default.join(import_os.default.tmpdir(), `pw-${userNameHash}`);
82
+ const dir = import_path.default.join(baseDir, domain);
83
+ const result = import_path.default.join(dir, `${name}.sock`);
84
+ import_fs.default.mkdirSync(dir, { recursive: true });
85
+ return result;
86
+ }
75
87
  class SerializedFS {
76
88
  constructor() {
77
89
  this._buffers = /* @__PURE__ */ new Map();
@@ -165,7 +177,8 @@ class SerializedFS {
165
177
  return;
166
178
  }
167
179
  case "zip": {
168
- const zipFile = new import_zipBundle.yazl.ZipFile();
180
+ const { yazl } = await import("playwright-core/lib/zipBundle");
181
+ const zipFile = new yazl.ZipFile();
169
182
  const result = new import_manualPromise.ManualPromise();
170
183
  zipFile.on("error", (error) => result.reject(error));
171
184
  for (const entry of op.entries)
@@ -184,6 +197,7 @@ class SerializedFS {
184
197
  canAccessFile,
185
198
  copyFileAndMakeWritable,
186
199
  existsAsync,
200
+ makeSocketPath,
187
201
  mkdirIfNeeded,
188
202
  removeFolders,
189
203
  sanitizeForFilePath,
@@ -166,20 +166,23 @@ async function createConnectionAsync(options, oncreate, useTLS) {
166
166
  }
167
167
  }
168
168
  async function lookupAddresses(hostname) {
169
- const addresses = await import_dns.default.promises.lookup(hostname, { all: true, family: 0, verbatim: true });
170
- let firstFamily = addresses.filter(({ family }) => family === 6);
171
- let secondFamily = addresses.filter(({ family }) => family === 4);
172
- if (firstFamily.length && firstFamily[0] !== addresses[0]) {
173
- const tmp = firstFamily;
174
- firstFamily = secondFamily;
175
- secondFamily = tmp;
169
+ const [v4Result, v6Result] = await Promise.allSettled([
170
+ import_dns.default.promises.lookup(hostname, { all: true, family: 4 }),
171
+ import_dns.default.promises.lookup(hostname, { all: true, family: 6 })
172
+ ]);
173
+ const v4Addresses = v4Result.status === "fulfilled" ? v4Result.value : [];
174
+ const v6Addresses = v6Result.status === "fulfilled" ? v6Result.value : [];
175
+ if (!v4Addresses.length && !v6Addresses.length) {
176
+ if (v4Result.status === "rejected")
177
+ throw v4Result.reason;
178
+ throw v6Result.reason;
176
179
  }
177
180
  const result = [];
178
- for (let i = 0; i < Math.max(firstFamily.length, secondFamily.length); i++) {
179
- if (firstFamily[i])
180
- result.push(firstFamily[i]);
181
- if (secondFamily[i])
182
- result.push(secondFamily[i]);
181
+ for (let i = 0; i < Math.max(v4Addresses.length, v6Addresses.length); i++) {
182
+ if (v6Addresses[i])
183
+ result.push(v6Addresses[i]);
184
+ if (v4Addresses[i])
185
+ result.push(v4Addresses[i]);
183
186
  }
184
187
  return result;
185
188
  }
@@ -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,28 +58,16 @@ 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
- createWebSocket(transport, guid) {
61
+ createWebSocket(transportFactory, 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)();
79
64
  const wss = new import_utilsBundle.wsServer({ server: this._server, path: "/" + this._wsGuid });
80
- wss.on("connection", (ws) => {
81
- transport.onconnect();
65
+ wss.on("connection", (ws, request) => {
66
+ const url = new URL(request.url ?? "/", "http://localhost");
67
+ const transport = transportFactory(url);
82
68
  transport.sendEvent = (method, params) => ws.send(JSON.stringify({ method, params }));
83
69
  transport.close = () => ws.close();
70
+ transport.onconnect();
84
71
  ws.on("message", async (message) => {
85
72
  const { id, method, params } = JSON.parse(String(message));
86
73
  try {
@@ -100,17 +87,17 @@ class HttpServer {
100
87
  async start(options = {}) {
101
88
  (0, import_assert.assert)(!this._started, "server already started");
102
89
  this._started = true;
103
- const host = options.host || "localhost";
90
+ const host = options.host;
104
91
  if (options.preferredPort) {
105
92
  try {
106
- await this._tryStart(options.preferredPort, host);
93
+ await (0, import_network.startHttpServer)(this._server, { port: options.preferredPort, host });
107
94
  } catch (e) {
108
95
  if (!e || !e.message || !e.message.includes("EADDRINUSE"))
109
96
  throw e;
110
- await this._tryStart(void 0, host);
97
+ await (0, import_network.startHttpServer)(this._server, { host });
111
98
  }
112
99
  } else {
113
- await this._tryStart(options.port, host);
100
+ await (0, import_network.startHttpServer)(this._server, { port: options.port, host });
114
101
  }
115
102
  const address = this._server.address();
116
103
  (0, import_assert.assert)(address, "Could not bind server socket");
@@ -121,7 +108,7 @@ class HttpServer {
121
108
  this._port = address.port;
122
109
  const resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
123
110
  this._urlPrefixPrecise = `http://${resolvedHost}:${address.port}`;
124
- this._urlPrefixHumanReadable = `http://${host}:${address.port}`;
111
+ this._urlPrefixHumanReadable = `http://${host ?? "localhost"}:${address.port}`;
125
112
  }
126
113
  }
127
114
  async stop() {
@@ -33,24 +33,23 @@ __export(network_exports, {
33
33
  createHttpServer: () => createHttpServer,
34
34
  createHttpsServer: () => createHttpsServer,
35
35
  createProxyAgent: () => createProxyAgent,
36
+ decorateServer: () => decorateServer,
36
37
  fetchData: () => fetchData,
37
38
  httpRequest: () => httpRequest,
38
- isURLAvailable: () => isURLAvailable
39
+ isURLAvailable: () => isURLAvailable,
40
+ startHttpServer: () => startHttpServer
39
41
  });
40
42
  module.exports = __toCommonJS(network_exports);
41
43
  var import_http = __toESM(require("http"));
42
44
  var import_http2 = __toESM(require("http2"));
43
45
  var import_https = __toESM(require("https"));
44
- var import_url = __toESM(require("url"));
45
46
  var import_utilsBundle = require("../../utilsBundle");
46
47
  var import_happyEyeballs = require("./happyEyeballs");
47
48
  var import_manualPromise = require("../../utils/isomorphic/manualPromise");
48
49
  const NET_DEFAULT_TIMEOUT = 3e4;
49
50
  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,
51
+ let url = new URL(params.url);
52
+ const options = {
54
53
  method: params.method || "GET",
55
54
  headers: params.headers
56
55
  };
@@ -58,21 +57,15 @@ function httpRequest(params, onResponse, onError) {
58
57
  options.rejectUnauthorized = params.rejectUnauthorized;
59
58
  const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
60
59
  if (proxyURL) {
60
+ const parsedProxyURL = normalizeProxyURL(proxyURL);
61
61
  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
- };
62
+ parsedProxyURL.pathname = url.toString();
63
+ url = parsedProxyURL;
71
64
  } else {
72
- options.agent = new import_utilsBundle.HttpsProxyAgent(normalizeProxyURL(proxyURL));
73
- options.rejectUnauthorized = false;
65
+ options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
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
  });
@@ -227,7 +235,9 @@ function decorateServer(server) {
227
235
  createHttpServer,
228
236
  createHttpsServer,
229
237
  createProxyAgent,
238
+ decorateServer,
230
239
  fetchData,
231
240
  httpRequest,
232
- isURLAvailable
241
+ isURLAvailable,
242
+ startHttpServer
233
243
  });
@@ -46,11 +46,12 @@ async function gracefullyCloseAll() {
46
46
  await Promise.all(Array.from(gracefullyCloseSet).map((gracefullyClose) => gracefullyClose().catch((e) => {
47
47
  })));
48
48
  }
49
- function gracefullyProcessExitDoNotHang(code) {
50
- setTimeout(() => process.exit(code), 3e4);
51
- gracefullyCloseAll().then(() => {
52
- process.exit(code);
53
- });
49
+ function gracefullyProcessExitDoNotHang(code, onExit) {
50
+ const beforeExit = onExit ? () => onExit().catch(() => {
51
+ }) : () => Promise.resolve();
52
+ const callback = () => beforeExit().then(() => process.exit(code));
53
+ setTimeout(callback, 3e4);
54
+ gracefullyCloseAll().then(callback);
54
55
  }
55
56
  function exitHandler() {
56
57
  for (const kill of killSet)
@@ -109,6 +110,7 @@ async function launchProcess(options) {
109
110
  // process group, making it possible to kill child process tree with `.kill(-pid)` command.
110
111
  // @see https://nodejs.org/api/child_process.html#child_process_options_detached
111
112
  detached: process.platform !== "win32",
113
+ windowsHide: true,
112
114
  env: options.env,
113
115
  cwd: options.cwd,
114
116
  shell: options.shell,
@@ -191,7 +193,7 @@ async function launchProcess(options) {
191
193
  options.log(`[pid=${spawnedProcess.pid}] <will force kill>`);
192
194
  try {
193
195
  if (process.platform === "win32") {
194
- const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true });
196
+ const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true, windowsHide: true });
195
197
  const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()];
196
198
  if (stdout2)
197
199
  options.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`);
@@ -21,7 +21,6 @@ __export(zipFile_exports, {
21
21
  ZipFile: () => ZipFile
22
22
  });
23
23
  module.exports = __toCommonJS(zipFile_exports);
24
- var import_zipBundle = require("../../zipBundle");
25
24
  class ZipFile {
26
25
  constructor(fileName) {
27
26
  this._entries = /* @__PURE__ */ new Map();
@@ -29,8 +28,9 @@ class ZipFile {
29
28
  this._openedPromise = this._open();
30
29
  }
31
30
  async _open() {
31
+ const { yauzl } = require("../../zipBundle");
32
32
  await new Promise((fulfill, reject) => {
33
- import_zipBundle.yauzl.open(this._fileName, { autoClose: false }, (e, z) => {
33
+ yauzl.open(this._fileName, { autoClose: false }, (e, z) => {
34
34
  if (e) {
35
35
  reject(e);
36
36
  return;
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var videoRecorder_exports = {};
30
+ __export(videoRecorder_exports, {
31
+ VideoRecorder: () => VideoRecorder,
32
+ startAutomaticVideoRecording: () => startAutomaticVideoRecording
33
+ });
34
+ module.exports = __toCommonJS(videoRecorder_exports);
35
+ var import_path = __toESM(require("path"));
36
+ var import_utils = require("../utils");
37
+ var import_processLauncher = require("./utils/processLauncher");
38
+ var import_utilsBundle = require("../utilsBundle");
39
+ var import_artifact = require("./artifact");
40
+ var import__ = require(".");
41
+ const fps = 25;
42
+ class VideoRecorder {
43
+ constructor(screencast) {
44
+ this._screencast = screencast;
45
+ }
46
+ start(options) {
47
+ (0, import_utils.assert)(!this._artifact);
48
+ const ffmpegPath = import__.registry.findExecutable("ffmpeg").executablePathOrDie(this._screencast.page.browserContext._browser.sdkLanguage());
49
+ const outputFile = options.fileName ?? import_path.default.join(this._screencast.page.browserContext._browser.options.artifactsDir, (0, import_utils.createGuid)() + ".webm");
50
+ this._client = {
51
+ onFrame: (frame) => this._videoRecorder.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3),
52
+ gracefulClose: () => this.stop(),
53
+ dispose: () => this.stop().catch((e) => import_utils.debugLogger.log("error", `Failed to stop video recorder: ${String(e)}`)),
54
+ size: options.size
55
+ };
56
+ const { size } = this._screencast.addClient(this._client);
57
+ const videoSize = options.size ?? size;
58
+ this._videoRecorder = new FfmpegVideoRecorder(ffmpegPath, videoSize, outputFile);
59
+ this._artifact = new import_artifact.Artifact(this._screencast.page.browserContext, outputFile);
60
+ return this._artifact;
61
+ }
62
+ async stop() {
63
+ if (!this._artifact)
64
+ return;
65
+ const artifact = this._artifact;
66
+ this._artifact = void 0;
67
+ const client = this._client;
68
+ this._client = void 0;
69
+ const videoRecorder = this._videoRecorder;
70
+ this._videoRecorder = void 0;
71
+ this._screencast.removeClient(client);
72
+ await videoRecorder.stop();
73
+ await artifact.reportFinished();
74
+ }
75
+ }
76
+ function startAutomaticVideoRecording(page) {
77
+ const recordVideo = page.browserContext._options.recordVideo;
78
+ if (!recordVideo)
79
+ return;
80
+ const recorder = new VideoRecorder(page.screencast);
81
+ if (page.browserContext._options.recordVideo?.showActions)
82
+ page.screencast.showActions(page.browserContext._options.recordVideo?.showActions);
83
+ const dir = recordVideo.dir ?? page.browserContext._browser.options.artifactsDir;
84
+ const artifact = recorder.start({ size: recordVideo.size, fileName: import_path.default.join(dir, page.guid + ".webm") });
85
+ page.video = artifact;
86
+ }
87
+ class FfmpegVideoRecorder {
88
+ constructor(ffmpegPath, size, outputFile) {
89
+ this._process = null;
90
+ this._gracefullyClose = null;
91
+ this._lastWritePromise = Promise.resolve();
92
+ this._firstFrameTimestamp = 0;
93
+ this._lastFrame = null;
94
+ this._lastWriteNodeTime = 0;
95
+ this._frameQueue = [];
96
+ this._isStopped = false;
97
+ if (!outputFile.endsWith(".webm"))
98
+ throw new Error("File must have .webm extension");
99
+ this._outputFile = outputFile;
100
+ this._ffmpegPath = ffmpegPath;
101
+ this._size = size;
102
+ this._launchPromise = this._launch().catch((e) => e);
103
+ }
104
+ async _launch() {
105
+ await (0, import_utils.mkdirIfNeeded)(this._outputFile);
106
+ const w = this._size.width;
107
+ const h = this._size.height;
108
+ 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(" ");
109
+ args.push(this._outputFile);
110
+ const { launchedProcess, gracefullyClose } = await (0, import_processLauncher.launchProcess)({
111
+ command: this._ffmpegPath,
112
+ args,
113
+ stdio: "stdin",
114
+ log: (message) => import_utils.debugLogger.log("browser", message),
115
+ tempDirectories: [],
116
+ attemptToGracefullyClose: async () => {
117
+ import_utils.debugLogger.log("browser", "Closing stdin...");
118
+ launchedProcess.stdin.end();
119
+ },
120
+ onExit: (exitCode, signal) => {
121
+ import_utils.debugLogger.log("browser", `ffmpeg onkill exitCode=${exitCode} signal=${signal}`);
122
+ }
123
+ });
124
+ launchedProcess.stdin.on("finish", () => {
125
+ import_utils.debugLogger.log("browser", "ffmpeg finished input.");
126
+ });
127
+ launchedProcess.stdin.on("error", () => {
128
+ import_utils.debugLogger.log("browser", "ffmpeg error.");
129
+ });
130
+ this._process = launchedProcess;
131
+ this._gracefullyClose = gracefullyClose;
132
+ }
133
+ writeFrame(frame, timestamp) {
134
+ this._launchPromise.then((error) => {
135
+ if (error)
136
+ return;
137
+ this._writeFrame(frame, timestamp);
138
+ });
139
+ }
140
+ _writeFrame(frame, timestamp) {
141
+ (0, import_utils.assert)(this._process);
142
+ if (this._isStopped)
143
+ return;
144
+ if (!this._firstFrameTimestamp)
145
+ this._firstFrameTimestamp = timestamp;
146
+ const frameNumber = Math.floor((timestamp - this._firstFrameTimestamp) * fps);
147
+ if (this._lastFrame) {
148
+ const repeatCount = frameNumber - this._lastFrame.frameNumber;
149
+ for (let i = 0; i < repeatCount; ++i)
150
+ this._frameQueue.push(this._lastFrame.buffer);
151
+ this._lastWritePromise = this._lastWritePromise.then(() => this._sendFrames());
152
+ }
153
+ this._lastFrame = { buffer: frame, timestamp, frameNumber };
154
+ this._lastWriteNodeTime = (0, import_utils.monotonicTime)();
155
+ }
156
+ async _sendFrames() {
157
+ while (this._frameQueue.length)
158
+ await this._sendFrame(this._frameQueue.shift());
159
+ }
160
+ async _sendFrame(frame) {
161
+ return new Promise((f) => this._process.stdin.write(frame, f)).then((error) => {
162
+ if (error)
163
+ import_utils.debugLogger.log("browser", `ffmpeg failed to write: ${String(error)}`);
164
+ });
165
+ }
166
+ async stop() {
167
+ const error = await this._launchPromise;
168
+ if (error)
169
+ throw error;
170
+ if (this._isStopped)
171
+ return;
172
+ if (!this._lastFrame) {
173
+ this._writeFrame(createWhiteImage(this._size.width, this._size.height), (0, import_utils.monotonicTime)());
174
+ }
175
+ const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
176
+ this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
177
+ this._isStopped = true;
178
+ try {
179
+ await this._lastWritePromise;
180
+ await this._gracefullyClose();
181
+ } catch (e) {
182
+ import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
183
+ }
184
+ }
185
+ }
186
+ function createWhiteImage(width, height) {
187
+ const data = Buffer.alloc(width * height * 4, 255);
188
+ return import_utilsBundle.jpegjs.encode({ data, width, height }, 80).data;
189
+ }
190
+ // Annotate the CommonJS export names for ESM import in node:
191
+ 0 && (module.exports = {
192
+ VideoRecorder,
193
+ startAutomaticVideoRecording
194
+ });