playwright-core 1.55.0-alpha-2025-07-29 → 1.55.0-alpha-1753913825000

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 (46) hide show
  1. package/browsers.json +8 -8
  2. package/lib/generated/injectedScriptSource.js +1 -1
  3. package/lib/generated/pollingRecorderSource.js +1 -1
  4. package/lib/protocol/validator.js +0 -16
  5. package/lib/server/android/android.js +54 -43
  6. package/lib/server/bidi/bidiPage.js +4 -0
  7. package/lib/server/browser.js +22 -18
  8. package/lib/server/browserContext.js +73 -46
  9. package/lib/server/browserType.js +67 -48
  10. package/lib/server/chromium/chromium.js +34 -28
  11. package/lib/server/chromium/crCoverage.js +3 -4
  12. package/lib/server/chromium/crDragDrop.js +17 -13
  13. package/lib/server/chromium/videoRecorder.js +10 -19
  14. package/lib/server/codegen/jsonl.js +2 -1
  15. package/lib/server/debugController.js +2 -18
  16. package/lib/server/deviceDescriptorsSource.json +2 -2
  17. package/lib/server/dispatchers/debugControllerDispatcher.js +0 -9
  18. package/lib/server/dispatchers/localUtilsDispatcher.js +0 -5
  19. package/lib/server/electron/electron.js +66 -66
  20. package/lib/server/fetch.js +6 -2
  21. package/lib/server/frames.js +6 -6
  22. package/lib/server/helper.js +4 -7
  23. package/lib/server/localUtils.js +23 -16
  24. package/lib/server/playwright.js +0 -4
  25. package/lib/server/progress.js +17 -11
  26. package/lib/server/recorder/recorderUtils.js +5 -0
  27. package/lib/server/recorder.js +61 -1
  28. package/lib/server/screenshotter.js +72 -56
  29. package/lib/server/socksClientCertificatesInterceptor.js +59 -43
  30. package/lib/server/transport.js +19 -15
  31. package/lib/server/utils/network.js +28 -17
  32. package/lib/server/utils/processLauncher.js +4 -1
  33. package/lib/utils/isomorphic/ariaSnapshot.js +58 -4
  34. package/lib/utils/isomorphic/protocolMetainfo.js +1 -5
  35. package/lib/utils/isomorphic/urlMatch.js +0 -2
  36. package/lib/vite/htmlReport/index.html +16 -16
  37. package/lib/vite/recorder/assets/{codeMirrorModule-cgtwtYOb.js → codeMirrorModule-B5Ye5Cij.js} +1 -1
  38. package/lib/vite/recorder/assets/{index-KWWSfrzB.js → index-B5VmX9JT.js} +48 -48
  39. package/lib/vite/recorder/index.html +1 -1
  40. package/lib/vite/traceViewer/assets/{codeMirrorModule-k_7BpzGX.js → codeMirrorModule-3XE5WU2G.js} +1 -1
  41. package/lib/vite/traceViewer/assets/{defaultSettingsView-CKM53V_K.js → defaultSettingsView-u5uZPktL.js} +103 -103
  42. package/lib/vite/traceViewer/{index.CCXGYfh2.js → index.CycYDQ3P.js} +1 -1
  43. package/lib/vite/traceViewer/index.html +2 -2
  44. package/lib/vite/traceViewer/{uiMode.CNa3x5Xj.js → uiMode.DeaA8YiP.js} +1 -1
  45. package/lib/vite/traceViewer/uiMode.html +2 -2
  46. package/package.json +1 -1
@@ -166,20 +166,20 @@ class Screenshotter {
166
166
  progress.log("taking page screenshot");
167
167
  const viewportSize = await this._originalViewportSize(progress);
168
168
  await this._preparePageForScreenshot(progress, this._page.mainFrame(), options.style, options.caret !== "initial", options.animations === "disabled");
169
- if (options.fullPage) {
170
- const fullPageSize = await this._fullPageSize(progress);
171
- let documentRect = { x: 0, y: 0, width: fullPageSize.width, height: fullPageSize.height };
172
- const fitsViewport = fullPageSize.width <= viewportSize.width && fullPageSize.height <= viewportSize.height;
173
- if (options.clip)
174
- documentRect = trimClipToSize(options.clip, documentRect);
175
- const buffer2 = await this._screenshot(progress, format, documentRect, void 0, fitsViewport, options);
169
+ try {
170
+ if (options.fullPage) {
171
+ const fullPageSize = await this._fullPageSize(progress);
172
+ let documentRect = { x: 0, y: 0, width: fullPageSize.width, height: fullPageSize.height };
173
+ const fitsViewport = fullPageSize.width <= viewportSize.width && fullPageSize.height <= viewportSize.height;
174
+ if (options.clip)
175
+ documentRect = trimClipToSize(options.clip, documentRect);
176
+ return await this._screenshot(progress, format, documentRect, void 0, fitsViewport, options);
177
+ }
178
+ const viewportRect = options.clip ? trimClipToSize(options.clip, viewportSize) : { x: 0, y: 0, ...viewportSize };
179
+ return await this._screenshot(progress, format, void 0, viewportRect, true, options);
180
+ } finally {
176
181
  await this._restorePageAfterScreenshot();
177
- return buffer2;
178
182
  }
179
- const viewportRect = options.clip ? trimClipToSize(options.clip, viewportSize) : { x: 0, y: 0, ...viewportSize };
180
- const buffer = await this._screenshot(progress, format, void 0, viewportRect, true, options);
181
- await this._restorePageAfterScreenshot();
182
- return buffer;
183
183
  });
184
184
  }
185
185
  async screenshotElement(progress, handle, options) {
@@ -188,36 +188,42 @@ class Screenshotter {
188
188
  progress.log("taking element screenshot");
189
189
  const viewportSize = await this._originalViewportSize(progress);
190
190
  await this._preparePageForScreenshot(progress, handle._frame, options.style, options.caret !== "initial", options.animations === "disabled");
191
- await handle._waitAndScrollIntoViewIfNeeded(
192
- progress,
193
- true
194
- /* waitForVisible */
195
- );
196
- const boundingBox = await progress.race(handle.boundingBox());
197
- (0, import_utils.assert)(boundingBox, "Node is either not visible or not an HTMLElement");
198
- (0, import_utils.assert)(boundingBox.width !== 0, "Node has 0 width.");
199
- (0, import_utils.assert)(boundingBox.height !== 0, "Node has 0 height.");
200
- const fitsViewport = boundingBox.width <= viewportSize.width && boundingBox.height <= viewportSize.height;
201
- const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({ x: window.scrollX, y: window.scrollY }));
202
- const documentRect = { ...boundingBox };
203
- documentRect.x += scrollOffset.x;
204
- documentRect.y += scrollOffset.y;
205
- const buffer = await this._screenshot(progress, format, import_helper.helper.enclosingIntRect(documentRect), void 0, fitsViewport, options);
206
- await this._restorePageAfterScreenshot();
207
- return buffer;
191
+ try {
192
+ await handle._waitAndScrollIntoViewIfNeeded(
193
+ progress,
194
+ true
195
+ /* waitForVisible */
196
+ );
197
+ const boundingBox = await progress.race(handle.boundingBox());
198
+ (0, import_utils.assert)(boundingBox, "Node is either not visible or not an HTMLElement");
199
+ (0, import_utils.assert)(boundingBox.width !== 0, "Node has 0 width.");
200
+ (0, import_utils.assert)(boundingBox.height !== 0, "Node has 0 height.");
201
+ const fitsViewport = boundingBox.width <= viewportSize.width && boundingBox.height <= viewportSize.height;
202
+ const scrollOffset = await this._page.mainFrame().waitForFunctionValueInUtility(progress, () => ({ x: window.scrollX, y: window.scrollY }));
203
+ const documentRect = { ...boundingBox };
204
+ documentRect.x += scrollOffset.x;
205
+ documentRect.y += scrollOffset.y;
206
+ return await this._screenshot(progress, format, import_helper.helper.enclosingIntRect(documentRect), void 0, fitsViewport, options);
207
+ } finally {
208
+ await this._restorePageAfterScreenshot();
209
+ }
208
210
  });
209
211
  }
210
212
  async _preparePageForScreenshot(progress, frame, screenshotStyle, hideCaret, disableAnimations) {
211
213
  if (disableAnimations)
212
214
  progress.log(" disabled all CSS animations");
213
215
  const syncAnimations = this._page.delegate.shouldToggleStyleSheetToSyncAnimations();
214
- progress.cleanupWhenAborted(() => this._restorePageAfterScreenshot());
215
216
  await progress.race(this._page.safeNonStallingEvaluateInAllFrames("(" + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, "utility"));
216
- if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
217
- progress.log("waiting for fonts to load...");
218
- await progress.race(frame.nonStallingEvaluateInExistingContext("document.fonts.ready", "utility").catch(() => {
219
- }));
220
- progress.log("fonts loaded");
217
+ try {
218
+ if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
219
+ progress.log("waiting for fonts to load...");
220
+ await progress.race(frame.nonStallingEvaluateInExistingContext("document.fonts.ready", "utility").catch(() => {
221
+ }));
222
+ progress.log("fonts loaded");
223
+ }
224
+ } catch (error) {
225
+ await this._restorePageAfterScreenshot();
226
+ throw error;
221
227
  }
222
228
  }
223
229
  async _restorePageAfterScreenshot() {
@@ -227,39 +233,49 @@ class Screenshotter {
227
233
  if (!options.mask || !options.mask.length)
228
234
  return () => Promise.resolve();
229
235
  const framesToParsedSelectors = new import_multimap.MultiMap();
230
- const cleanup = async () => {
231
- await Promise.all([...framesToParsedSelectors.keys()].map(async (frame) => {
232
- await frame.hideHighlight();
233
- }));
234
- };
235
- progress.cleanupWhenAborted(cleanup);
236
236
  await progress.race(Promise.all((options.mask || []).map(async ({ frame, selector }) => {
237
237
  const pair = await frame.selectors.resolveFrameForSelector(selector);
238
238
  if (pair)
239
239
  framesToParsedSelectors.set(pair.frame, pair.info.parsed);
240
240
  })));
241
- await progress.race(Promise.all([...framesToParsedSelectors.keys()].map(async (frame) => {
242
- await frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor || "#F0F");
243
- })));
244
- return cleanup;
241
+ const frames = [...framesToParsedSelectors.keys()];
242
+ const cleanup = async () => {
243
+ await Promise.all(frames.map((frame) => frame.hideHighlight()));
244
+ };
245
+ try {
246
+ const promises = frames.map((frame) => frame.maskSelectors(framesToParsedSelectors.get(frame), options.maskColor || "#F0F"));
247
+ await progress.race(Promise.all(promises));
248
+ return cleanup;
249
+ } catch (error) {
250
+ cleanup().catch(() => {
251
+ });
252
+ throw error;
253
+ }
245
254
  }
246
255
  async _screenshot(progress, format, documentRect, viewportRect, fitsViewport, options) {
247
256
  if (options.__testHookBeforeScreenshot)
248
257
  await progress.race(options.__testHookBeforeScreenshot());
249
258
  const shouldSetDefaultBackground = options.omitBackground && format === "png";
250
- if (shouldSetDefaultBackground) {
251
- progress.cleanupWhenAborted(() => this._page.delegate.setBackgroundColor());
259
+ if (shouldSetDefaultBackground)
252
260
  await progress.race(this._page.delegate.setBackgroundColor({ r: 0, g: 0, b: 0, a: 0 }));
253
- }
254
261
  const cleanupHighlight = await this._maskElements(progress, options);
255
- const quality = format === "jpeg" ? options.quality ?? 80 : void 0;
256
- const buffer = await this._page.delegate.takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, options.scale || "device");
257
- await cleanupHighlight();
258
- if (shouldSetDefaultBackground)
259
- await progress.race(this._page.delegate.setBackgroundColor());
260
- if (options.__testHookAfterScreenshot)
261
- await progress.race(options.__testHookAfterScreenshot());
262
- return buffer;
262
+ try {
263
+ const quality = format === "jpeg" ? options.quality ?? 80 : void 0;
264
+ const buffer = await this._page.delegate.takeScreenshot(progress, format, documentRect, viewportRect, quality, fitsViewport, options.scale || "device");
265
+ await cleanupHighlight();
266
+ if (shouldSetDefaultBackground)
267
+ await this._page.delegate.setBackgroundColor();
268
+ if (options.__testHookAfterScreenshot)
269
+ await progress.race(options.__testHookAfterScreenshot());
270
+ return buffer;
271
+ } catch (error) {
272
+ cleanupHighlight().catch(() => {
273
+ });
274
+ if (shouldSetDefaultBackground)
275
+ this._page.delegate.setBackgroundColor().catch(() => {
276
+ });
277
+ throw error;
278
+ }
263
279
  }
264
280
  }
265
281
  class TaskQueue {
@@ -44,6 +44,7 @@ var import_browserContext = require("./browserContext");
44
44
  var import_network = require("./utils/network");
45
45
  var import_debugLogger = require("./utils/debugLogger");
46
46
  var import_happyEyeballs = require("./utils/happyEyeballs");
47
+ var import_utilsBundle = require("../utilsBundle");
47
48
  let dummyServerTlsOptions = void 0;
48
49
  function loadDummyServerCertsIfNeeded() {
49
50
  if (dummyServerTlsOptions)
@@ -84,7 +85,7 @@ class ALPNCache {
84
85
  }
85
86
  class SocksProxyConnection {
86
87
  constructor(socksProxy, uid, host, port) {
87
- this.firstPackageReceived = false;
88
+ this._firstPackageReceived = false;
88
89
  this._closed = false;
89
90
  this.socksProxy = socksProxy;
90
91
  this.uid = uid;
@@ -92,55 +93,57 @@ class SocksProxyConnection {
92
93
  this.port = port;
93
94
  this._targetCloseEventListener = () => {
94
95
  this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid });
95
- this.internalTLS?.destroy();
96
+ this._internalTLS?.destroy();
96
97
  this._dummyServer?.close();
97
98
  };
99
+ this._internal = new import_stream.default.Duplex({
100
+ read: () => {
101
+ },
102
+ write: (data, encoding, callback) => {
103
+ this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data });
104
+ callback();
105
+ }
106
+ });
98
107
  }
99
108
  async connect() {
100
- if (this.socksProxy.proxyAgentFromOptions)
101
- this.target = await this.socksProxy.proxyAgentFromOptions.callback(new import_events.EventEmitter(), { host: rewriteToLocalhostIfNeeded(this.host), port: this.port, secureEndpoint: false });
109
+ const proxyAgent = this.socksProxy.getProxyAgent(this.host, this.port);
110
+ if (proxyAgent)
111
+ this._target = await proxyAgent.callback(new import_events.EventEmitter(), { host: rewriteToLocalhostIfNeeded(this.host), port: this.port, secureEndpoint: false });
102
112
  else
103
- this.target = await (0, import_happyEyeballs.createSocket)(rewriteToLocalhostIfNeeded(this.host), this.port);
104
- this.target.once("close", this._targetCloseEventListener);
105
- this.target.once("error", (error) => this.socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message }));
113
+ this._target = await (0, import_happyEyeballs.createSocket)(rewriteToLocalhostIfNeeded(this.host), this.port);
114
+ this._target.once("close", this._targetCloseEventListener);
115
+ this._target.once("error", (error) => this.socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message }));
106
116
  if (this._closed) {
107
- this.target.destroy();
117
+ this._target.destroy();
108
118
  return;
109
119
  }
110
120
  this.socksProxy._socksProxy.socketConnected({
111
121
  uid: this.uid,
112
- host: this.target.localAddress,
113
- port: this.target.localPort
122
+ host: this._target.localAddress,
123
+ port: this._target.localPort
114
124
  });
115
125
  }
116
126
  onClose() {
117
- this.target.destroy();
118
- this.internalTLS?.destroy();
127
+ this._target.destroy();
128
+ this._internalTLS?.destroy();
119
129
  this._dummyServer?.close();
120
130
  this._closed = true;
121
131
  }
122
132
  onData(data) {
123
- if (!this.firstPackageReceived) {
124
- this.firstPackageReceived = true;
133
+ if (!this._firstPackageReceived) {
134
+ this._firstPackageReceived = true;
125
135
  if (data[0] === 22)
126
- this._attachTLSListeners();
136
+ this._establishTlsTunnel(this._internal, data);
127
137
  else
128
- this.target.on("data", (data2) => this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data: data2 }));
138
+ this._establishPlaintextTunnel(this._internal);
129
139
  }
130
- if (this.internal)
131
- this.internal.push(data);
132
- else
133
- this.target.write(data);
140
+ this._internal.push(data);
134
141
  }
135
- _attachTLSListeners() {
136
- this.internal = new import_stream.default.Duplex({
137
- read: () => {
138
- },
139
- write: (data, encoding, callback) => {
140
- this.socksProxy._socksProxy.sendSocketData({ uid: this.uid, data });
141
- callback();
142
- }
143
- });
142
+ _establishPlaintextTunnel(internal) {
143
+ internal.pipe(this._target);
144
+ this._target.pipe(internal);
145
+ }
146
+ _establishTlsTunnel(internal, clientHello) {
144
147
  this.socksProxy.alpnCache.get(rewriteToLocalhostIfNeeded(this.host), this.port, (alpnProtocolChosenByServer) => {
145
148
  import_debugLogger.debugLogger.log("client-certificates", `Proxy->Target ${this.host}:${this.port} chooses ALPN ${alpnProtocolChosenByServer}`);
146
149
  if (this._closed)
@@ -149,9 +152,9 @@ class SocksProxyConnection {
149
152
  ...dummyServerTlsOptions,
150
153
  ALPNProtocols: alpnProtocolChosenByServer === "h2" ? ["h2", "http/1.1"] : ["http/1.1"]
151
154
  });
152
- this._dummyServer.emit("connection", this.internal);
155
+ this._dummyServer.emit("connection", this._internal);
153
156
  this._dummyServer.once("secureConnection", (internalTLS) => {
154
- this.internalTLS = internalTLS;
157
+ this._internalTLS = internalTLS;
155
158
  import_debugLogger.debugLogger.log("client-certificates", `Browser->Proxy ${this.host}:${this.port} chooses ALPN ${internalTLS.alpnProtocol}`);
156
159
  let targetTLS = void 0;
157
160
  const handleError = (error) => {
@@ -159,10 +162,10 @@ class SocksProxyConnection {
159
162
  const responseBody = (0, import_utils.escapeHTML)("Playwright client-certificate error: " + error.message).replaceAll("\n", " <br>");
160
163
  if (internalTLS?.alpnProtocol === "h2") {
161
164
  if ("performServerHandshake" in import_http2.default) {
162
- this.target.removeListener("close", this._targetCloseEventListener);
165
+ this._target.removeListener("close", this._targetCloseEventListener);
163
166
  const session = import_http2.default.performServerHandshake(internalTLS);
164
167
  session.on("error", () => {
165
- this.target.destroy();
168
+ this._target.destroy();
166
169
  this._targetCloseEventListener();
167
170
  });
168
171
  session.once("stream", (stream2) => {
@@ -172,14 +175,14 @@ class SocksProxyConnection {
172
175
  });
173
176
  const cleanup = () => {
174
177
  session.close();
175
- this.target.destroy();
178
+ this._target.destroy();
176
179
  this._targetCloseEventListener();
177
180
  };
178
181
  stream2.end(responseBody, cleanup);
179
182
  stream2.once("error", cleanup);
180
183
  });
181
184
  } else {
182
- this.target.destroy();
185
+ this._target.destroy();
183
186
  }
184
187
  } else {
185
188
  internalTLS.end([
@@ -189,7 +192,7 @@ class SocksProxyConnection {
189
192
  "",
190
193
  responseBody
191
194
  ].join("\r\n"));
192
- this.target.destroy();
195
+ this._target.destroy();
193
196
  }
194
197
  };
195
198
  if (this._closed) {
@@ -197,7 +200,7 @@ class SocksProxyConnection {
197
200
  return;
198
201
  }
199
202
  targetTLS = import_tls.default.connect({
200
- socket: this.target,
203
+ socket: this._target,
201
204
  host: this.host,
202
205
  port: this.port,
203
206
  rejectUnauthorized: !this.socksProxy.ignoreHTTPSErrors,
@@ -209,7 +212,7 @@ class SocksProxyConnection {
209
212
  internalTLS.pipe(targetTLS);
210
213
  targetTLS.pipe(internalTLS);
211
214
  });
212
- internalTLS.once("error", () => this.target.destroy());
215
+ internalTLS.once("error", () => this._target.destroy());
213
216
  targetTLS.once("error", handleError);
214
217
  });
215
218
  });
@@ -222,7 +225,7 @@ class ClientCertificatesProxy {
222
225
  (0, import_browserContext.verifyClientCertificates)(contextOptions.clientCertificates);
223
226
  this.alpnCache = new ALPNCache();
224
227
  this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors;
225
- this.proxyAgentFromOptions = (0, import_network.createProxyAgent)(contextOptions.proxy);
228
+ this._proxy = contextOptions.proxy;
226
229
  this._initSecureContexts(contextOptions.clientCertificates);
227
230
  this._socksProxy = new import_socksProxy.SocksProxy();
228
231
  this._socksProxy.setPattern("*");
@@ -236,7 +239,7 @@ class ClientCertificatesProxy {
236
239
  this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code });
237
240
  }
238
241
  });
239
- this._socksProxy.addListener(import_socksProxy.SocksProxy.Events.SocksData, async (payload) => {
242
+ this._socksProxy.addListener(import_socksProxy.SocksProxy.Events.SocksData, (payload) => {
240
243
  this._connections.get(payload.uid)?.onData(payload.data);
241
244
  });
242
245
  this._socksProxy.addListener(import_socksProxy.SocksProxy.Events.SocksClosed, (payload) => {
@@ -245,6 +248,14 @@ class ClientCertificatesProxy {
245
248
  });
246
249
  loadDummyServerCertsIfNeeded();
247
250
  }
251
+ getProxyAgent(host, port) {
252
+ const proxyFromOptions = (0, import_network.createProxyAgent)(this._proxy);
253
+ if (proxyFromOptions)
254
+ return proxyFromOptions;
255
+ const proxyFromEnv = (0, import_utilsBundle.getProxyForUrl)(`https://${host}:${port}`);
256
+ if (proxyFromEnv)
257
+ return (0, import_network.createProxyAgent)({ server: proxyFromEnv });
258
+ }
248
259
  _initSecureContexts(clientCertificates) {
249
260
  const origin2certs = /* @__PURE__ */ new Map();
250
261
  for (const cert of clientCertificates || []) {
@@ -262,10 +273,15 @@ class ClientCertificatesProxy {
262
273
  }
263
274
  }
264
275
  }
265
- static async create(contextOptions) {
276
+ static async create(progress, contextOptions) {
266
277
  const proxy = new ClientCertificatesProxy(contextOptions);
267
- await proxy._socksProxy.listen(0, "127.0.0.1");
268
- return proxy;
278
+ try {
279
+ await progress.race(proxy._socksProxy.listen(0, "127.0.0.1"));
280
+ return proxy;
281
+ } catch (error) {
282
+ await proxy.close();
283
+ throw error;
284
+ }
269
285
  }
270
286
  proxySettings() {
271
287
  return { server: `socks5://127.0.0.1:${this._socksProxy.port()}` };
@@ -97,11 +97,10 @@ class WebSocketTransport {
97
97
  const logUrl = stripQueryParams(url);
98
98
  progress?.log(`<ws connecting> ${logUrl}`);
99
99
  const transport = new WebSocketTransport(progress, url, logUrl, { ...options, followRedirects: !!options.followRedirects && hadRedirects });
100
- progress?.cleanupWhenAborted(() => transport.closeAndWait());
101
100
  const resultPromise = new Promise((fulfill, reject) => {
102
101
  transport._ws.on("open", async () => {
103
102
  progress?.log(`<ws connected> ${logUrl}`);
104
- fulfill({ transport });
103
+ fulfill({});
105
104
  });
106
105
  transport._ws.on("error", (event) => {
107
106
  progress?.log(`<ws connect error> ${logUrl} ${event.message}`);
@@ -130,20 +129,25 @@ ${Buffer.concat(chunks)}` : errorPrefix;
130
129
  });
131
130
  });
132
131
  });
133
- const result = progress ? await progress.race(resultPromise) : await resultPromise;
134
- if (result.redirect) {
135
- const newHeaders = Object.fromEntries(Object.entries(options.headers || {}).filter(([name]) => {
136
- return !name.includes("access-key") && name.toLowerCase() !== "authorization";
137
- }));
138
- return WebSocketTransport._connect(
139
- progress,
140
- result.redirect.headers.location,
141
- { ...options, headers: newHeaders },
142
- true
143
- /* hadRedirects */
144
- );
132
+ try {
133
+ const result = progress ? await progress.race(resultPromise) : await resultPromise;
134
+ if (result.redirect) {
135
+ const newHeaders = Object.fromEntries(Object.entries(options.headers || {}).filter(([name]) => {
136
+ return !name.includes("access-key") && name.toLowerCase() !== "authorization";
137
+ }));
138
+ return WebSocketTransport._connect(
139
+ progress,
140
+ result.redirect.headers.location,
141
+ { ...options, headers: newHeaders },
142
+ true
143
+ /* hadRedirects */
144
+ );
145
+ }
146
+ return transport;
147
+ } catch (error) {
148
+ await transport.closeAndWait();
149
+ throw error;
145
150
  }
146
- return transport;
147
151
  }
148
152
  send(message) {
149
153
  this._ws.send(JSON.stringify(message));
@@ -44,6 +44,7 @@ var import_https = __toESM(require("https"));
44
44
  var import_url = __toESM(require("url"));
45
45
  var import_utilsBundle = require("../../utilsBundle");
46
46
  var import_happyEyeballs = require("./happyEyeballs");
47
+ var import_manualPromise = require("../../utils/isomorphic/manualPromise");
47
48
  const NET_DEFAULT_TIMEOUT = 3e4;
48
49
  function httpRequest(params, onResponse, onError) {
49
50
  const parsedUrl = import_url.default.parse(params.url);
@@ -90,26 +91,36 @@ function httpRequest(params, onResponse, onError) {
90
91
  request.abort();
91
92
  });
92
93
  }
93
- cancelRequest = (e) => request.destroy(e);
94
+ cancelRequest = (e) => {
95
+ try {
96
+ request.destroy(e);
97
+ } catch {
98
+ }
99
+ };
94
100
  request.end(params.data);
95
101
  return { cancel: (e) => cancelRequest(e) };
96
102
  }
97
- function fetchData(progress, params, onError) {
98
- const promise = new Promise((resolve, reject) => {
99
- const { cancel } = httpRequest(params, async (response) => {
100
- if (response.statusCode !== 200) {
101
- const error = onError ? await onError(params, response) : new Error(`fetch failed: server returned code ${response.statusCode}. URL: ${params.url}`);
102
- reject(error);
103
- return;
104
- }
105
- let body = "";
106
- response.on("data", (chunk) => body += chunk);
107
- response.on("error", (error) => reject(error));
108
- response.on("end", () => resolve(body));
109
- }, reject);
110
- progress?.cleanupWhenAborted(cancel);
111
- });
112
- return progress ? progress.race(promise) : promise;
103
+ async function fetchData(progress, params, onError) {
104
+ const promise = new import_manualPromise.ManualPromise();
105
+ const { cancel } = httpRequest(params, async (response) => {
106
+ if (response.statusCode !== 200) {
107
+ const error = onError ? await onError(params, response) : new Error(`fetch failed: server returned code ${response.statusCode}. URL: ${params.url}`);
108
+ promise.reject(error);
109
+ return;
110
+ }
111
+ let body = "";
112
+ response.on("data", (chunk) => body += chunk);
113
+ response.on("error", (error) => promise.reject(error));
114
+ response.on("end", () => promise.resolve(body));
115
+ }, (error) => promise.reject(error));
116
+ if (!progress)
117
+ return promise;
118
+ try {
119
+ return await progress.race(promise);
120
+ } catch (error) {
121
+ cancel(error);
122
+ throw error;
123
+ }
113
124
  }
114
125
  function shouldBypassProxy(url2, bypass) {
115
126
  if (!bypass)
@@ -132,7 +132,10 @@ async function launchProcess(options) {
132
132
  spawnedProcess.once("error", (error) => {
133
133
  failed(new Error("Failed to launch: " + error));
134
134
  });
135
- return failedPromise.then((e) => Promise.reject(e));
135
+ return failedPromise.then(async (error) => {
136
+ await cleanup();
137
+ throw error;
138
+ });
136
139
  }
137
140
  options.log(`<launched> pid=${spawnedProcess.pid}`);
138
141
  const stdout = readline.createInterface({ input: spawnedProcess.stdout });
@@ -20,13 +20,14 @@ var ariaSnapshot_exports = {};
20
20
  __export(ariaSnapshot_exports, {
21
21
  KeyParser: () => KeyParser,
22
22
  ParserError: () => ParserError,
23
+ findNewElementRef: () => findNewElementRef,
23
24
  parseAriaSnapshot: () => parseAriaSnapshot,
24
25
  parseAriaSnapshotUnsafe: () => parseAriaSnapshotUnsafe,
25
26
  valueOrRegex: () => valueOrRegex
26
27
  });
27
28
  module.exports = __toCommonJS(ariaSnapshot_exports);
28
- function parseAriaSnapshotUnsafe(yaml, text) {
29
- const result = parseAriaSnapshot(yaml, text);
29
+ function parseAriaSnapshotUnsafe(yaml, text, options = {}) {
30
+ const result = parseAriaSnapshot(yaml, text, options);
30
31
  if (result.errors.length)
31
32
  throw new Error(result.errors[0].message);
32
33
  return result.fragment;
@@ -187,7 +188,7 @@ function valueOrRegex(value) {
187
188
  class KeyParser {
188
189
  static parse(text, options, errors) {
189
190
  try {
190
- return new KeyParser(text.value)._parse();
191
+ return new KeyParser(text.value, options)._parse();
191
192
  } catch (e) {
192
193
  if (e instanceof ParserError) {
193
194
  const message = options.prettyErrors === false ? e.message : e.message + ":\n\n" + text.value + "\n" + " ".repeat(e.pos) + "^\n";
@@ -200,10 +201,11 @@ class KeyParser {
200
201
  throw e;
201
202
  }
202
203
  }
203
- constructor(input) {
204
+ constructor(input, options) {
204
205
  this._input = input;
205
206
  this._pos = 0;
206
207
  this._length = input.length;
208
+ this._options = options;
207
209
  }
208
210
  _peek() {
209
211
  return this._input[this._pos] || "";
@@ -331,6 +333,10 @@ class KeyParser {
331
333
  return result;
332
334
  }
333
335
  _applyAttribute(node, key, value, errorPos) {
336
+ if (this._options.allowRef && key === "ref") {
337
+ node.ref = value;
338
+ return;
339
+ }
334
340
  if (key === "checked") {
335
341
  this._assert(value === "true" || value === "false" || value === "mixed", 'Value of "checked" attribute must be a boolean or "mixed"', errorPos);
336
342
  node.checked = value === "true" ? true : value === "false" ? false : "mixed";
@@ -366,6 +372,8 @@ class KeyParser {
366
372
  node.selected = value === "true";
367
373
  return;
368
374
  }
375
+ if (this._options.allowUnknownAttributes)
376
+ return;
369
377
  this._assert(false, `Unsupported attribute [${key}]`, errorPos);
370
378
  }
371
379
  _assert(value, message, valuePos) {
@@ -379,10 +387,56 @@ class ParserError extends Error {
379
387
  this.pos = pos;
380
388
  }
381
389
  }
390
+ function findNewElementRef(yaml, fromSnapshot, toSnapshot) {
391
+ function fillMap(root, map, position) {
392
+ let size = 1;
393
+ let childPosition = position + size;
394
+ for (const child of root.children || []) {
395
+ if (child.kind === "role") {
396
+ size += fillMap(child, map, childPosition);
397
+ childPosition += size;
398
+ } else {
399
+ size++;
400
+ childPosition++;
401
+ }
402
+ }
403
+ if (!["none", "presentation", "fragment", "iframe", "generic"].includes(root.role) && typeof root.name === "string" && root.name) {
404
+ let byRole = map.get(root.role);
405
+ if (!byRole) {
406
+ byRole = /* @__PURE__ */ new Map();
407
+ map.set(root.role, byRole);
408
+ }
409
+ const existing = byRole.get(root.name);
410
+ const sizeAndPosition = size * 100 - position;
411
+ if (!existing || existing.sizeAndPosition < sizeAndPosition)
412
+ byRole.set(root.name, { node: root, sizeAndPosition });
413
+ }
414
+ return size;
415
+ }
416
+ const fromMap = /* @__PURE__ */ new Map();
417
+ const from = parseAriaSnapshotUnsafe(yaml, fromSnapshot, { allowRef: true, allowUnknownAttributes: true });
418
+ if (from.kind === "role")
419
+ fillMap(from, fromMap, 0);
420
+ const toMap = /* @__PURE__ */ new Map();
421
+ const to = parseAriaSnapshotUnsafe(yaml, toSnapshot, { allowRef: true, allowUnknownAttributes: true });
422
+ if (to.kind === "role")
423
+ fillMap(to, toMap, 0);
424
+ const result = [];
425
+ for (const [role, byRole] of toMap) {
426
+ for (const [name, byName] of byRole) {
427
+ const inFrom = fromMap.get(role)?.get(name);
428
+ if (!inFrom)
429
+ result.push(byName);
430
+ }
431
+ }
432
+ result.sort((a, b) => b.sizeAndPosition - a.sizeAndPosition);
433
+ return result.find((r) => r.node.ref)?.node.ref;
434
+ }
382
435
  // Annotate the CommonJS export names for ESM import in node:
383
436
  0 && (module.exports = {
384
437
  KeyParser,
385
438
  ParserError,
439
+ findNewElementRef,
386
440
  parseAriaSnapshot,
387
441
  parseAriaSnapshotUnsafe,
388
442
  valueOrRegex