webdriver 9.3.1 → 9.4.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.
package/build/index.js CHANGED
@@ -1,131 +1,301 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
8
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
9
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
+ var __privateWrapper = (obj, member, setter, getter) => ({
13
+ set _(value) {
14
+ __privateSet(obj, member, value, setter);
15
+ },
16
+ get _() {
17
+ return __privateGet(obj, member, getter);
18
+ }
19
+ });
20
+
1
21
  // src/index.ts
2
- import logger5 from "@wdio/logger";
22
+ import logger4 from "@wdio/logger";
3
23
  import { webdriverMonad, sessionEnvironmentDetector, startWebDriver, isBidi } from "@wdio/utils";
4
24
  import { validateConfig } from "@wdio/config";
5
25
 
6
26
  // src/command.ts
7
- import logger4 from "@wdio/logger";
27
+ import logger from "@wdio/logger";
8
28
  import { commandCallStructure, isValidParameter, getArgumentType } from "@wdio/utils";
9
- import { WebDriverBidiProtocol as WebDriverBidiProtocol2 } from "@wdio/protocols";
10
-
11
- // src/request/request.ts
12
- import { performance } from "node:perf_hooks";
13
- import dns from "node:dns";
14
-
15
- // src/request/index.ts
16
- import path from "node:path";
17
- import { EventEmitter } from "node:events";
18
- import { WebDriverProtocol as WebDriverProtocol2 } from "@wdio/protocols";
19
- import { URL } from "node:url";
20
- import logger3 from "@wdio/logger";
21
- import { transformCommandLogResult as transformCommandLogResult2, sleep } from "@wdio/utils";
22
-
23
- // src/request/error.ts
24
- import { transformCommandLogResult } from "@wdio/utils";
25
-
26
- // src/request/constants.ts
27
- var RETRYABLE_STATUS_CODES = [408, 413, 429, 500, 502, 503, 504];
28
- var RETRYABLE_ERROR_CODES = [
29
- "ETIMEDOUT",
30
- "ECONNRESET",
31
- "EADDRINUSE",
32
- "ECONNREFUSED",
33
- "EPIPE",
34
- "ENOTFOUND",
35
- "ENETUNREACH",
36
- "EAI_AGAIN",
37
- // additional error codes we like to retry
38
- "UND_ERR_CONNECT_TIMEOUT",
39
- "UND_ERR_SOCKET"
40
- ];
41
- var REG_EXPS = {
42
- commandName: /.*\/session\/[0-9a-f-]+\/(.*)/,
43
- execFn: /return \(([\s\S]*)\)\.apply\(null, arguments\)/
44
- };
29
+ import { WebDriverBidiProtocol } from "@wdio/protocols";
45
30
 
46
- // src/request/error.ts
47
- var WebDriverError = class extends Error {
48
- /**
49
- * return timeout error with information about the executing command on which the test hangs
50
- */
51
- computeErrorMessage() {
52
- const cmdName = this.#getExecCmdName();
53
- const cmdArgs = this.#getExecCmdArgs(this.opts);
54
- const cmdInfoMsg = `when running "${cmdName}" with method "${this.opts.method}"`;
55
- const cmdArgsMsg = cmdArgs ? ` and args ${cmdArgs}` : "";
56
- return `WebDriverError: ${this.message} ${cmdInfoMsg}${cmdArgsMsg}`;
57
- }
58
- #getExecCmdName() {
59
- const { href } = this.url;
60
- const res = href.match(REG_EXPS.commandName) || [];
61
- return res[1] || href;
62
- }
63
- #getExecCmdArgs(requestOptions) {
64
- const { body: cmdJson } = requestOptions;
65
- if (typeof cmdJson !== "object") {
66
- return "";
67
- }
68
- const transformedRes = transformCommandLogResult(cmdJson);
69
- if (typeof transformedRes === "string") {
70
- return transformedRes;
71
- }
72
- if (typeof cmdJson.script === "string") {
73
- const scriptRes = cmdJson.script.match(REG_EXPS.execFn) || [];
74
- return `"${scriptRes[1] || cmdJson.script}"`;
31
+ // src/environment.ts
32
+ var isNode = !!(typeof process !== "undefined" && process.version);
33
+ var environment = {
34
+ value: {
35
+ get Request() {
36
+ throw new Error("Request is not available in this environment");
37
+ },
38
+ get Socket() {
39
+ throw new Error("Socket is not available in this environment");
40
+ },
41
+ get variables() {
42
+ return {};
75
43
  }
76
- return Object.keys(cmdJson).length ? `"${JSON.stringify(cmdJson)}"` : "";
77
44
  }
78
45
  };
79
- var WebDriverRequestError = class extends WebDriverError {
80
- url;
81
- opts;
82
- statusCode;
83
- body;
84
- code;
85
- constructor(err, url, opts) {
86
- let message = err.message;
87
- if (err.message === "fetch failed") {
88
- message = `Failed to fetch [${opts.method}] ${url.href}: please make sure you have a WebDriver compatible server running on ${url.origin}`;
46
+
47
+ // src/command.ts
48
+ var log = logger("webdriver");
49
+ var BIDI_COMMANDS = Object.values(WebDriverBidiProtocol).map((def) => def.socket.command);
50
+ function command_default(method, endpointUri, commandInfo, doubleEncodeVariables = false) {
51
+ const { command, deprecated, ref, parameters, variables = [], isHubCommand = false } = commandInfo;
52
+ return async function protocolCommand(...args) {
53
+ const isBidiCommand = BIDI_COMMANDS.includes(command);
54
+ let endpoint = endpointUri;
55
+ const commandParams = [...variables.map((v) => Object.assign(v, {
56
+ /**
57
+ * url variables are:
58
+ */
59
+ required: true,
60
+ // always required as they are part of the endpoint
61
+ type: "string"
62
+ // have to be always type of string
63
+ })), ...parameters];
64
+ const commandUsage = "".concat(command, "(").concat(commandParams.map((p) => p.name).join(", "), ")");
65
+ const moreInfo = "\n\nFor more info see ".concat(ref, "\n");
66
+ const body = {};
67
+ if (typeof deprecated === "string" && !process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS) {
68
+ const warning = deprecated.replace("This command", 'The "'.concat(command, '" command'));
69
+ log.warn(warning);
70
+ console.warn("\u26A0\uFE0F [WEBDRIVERIO DEPRECATION NOTICE] ".concat(warning));
89
71
  }
90
- super(message);
91
- this.url = url;
92
- this.opts = opts;
93
- const errorCode = typeof err.cause === "object" && err.cause && "code" in err.cause && typeof err.cause.code === "string" ? err.cause.code : "code" in err && typeof err.code === "string" ? err.code : void 0;
94
- if (errorCode) {
95
- this.code = errorCode;
96
- this.message = errorCode === "UND_ERR_CONNECT_TIMEOUT" ? 'Request timed out! Consider increasing the "connectionRetryTimeout" option.' : "Request failed with error code " + errorCode;
72
+ if (isBidiCommand) {
73
+ throw new Error(
74
+ 'Failed to execute WebDriver Bidi command "'.concat(command, '" as no Bidi session ') + 'was established. Make sure you enable it by setting "webSocketUrl: true" in your capabilities and verify that your environment and browser supports it.'
75
+ );
97
76
  }
98
- this.message = this.computeErrorMessage();
99
- }
100
- };
101
- var WebDriverResponseError = class _WebDriverResponseError extends WebDriverError {
102
- url;
103
- opts;
104
- constructor(response, url, opts) {
105
- const errorObj = !response.body ? new Error("Response has empty body") : typeof response.body === "string" && response.body.length ? new Error(response.body) : typeof response.body !== "object" ? new Error("Unknown error") : response.body.value || response.body;
106
- let errorMessage = errorObj.message || errorObj.error || errorObj.class || "unknown error";
107
- if (typeof errorMessage === "string" && errorMessage.includes("invalid locator")) {
108
- const requestOptions = opts.body;
109
- errorMessage = `The selector "${requestOptions.value}" used with strategy "${requestOptions.using}" is invalid!`;
77
+ const minAllowedParams = commandParams.filter((param) => param.required).length;
78
+ if (args.length < minAllowedParams || args.length > commandParams.length) {
79
+ const parameterDescription = commandParams.length ? "\n\nProperty Description:\n".concat(commandParams.map((p) => ' "'.concat(p.name, '" (').concat(p.type, "): ").concat(p.description)).join("\n")) : "";
80
+ throw new Error(
81
+ "Wrong parameters applied for ".concat(command, "\n") + "Usage: ".concat(commandUsage) + parameterDescription + moreInfo
82
+ );
110
83
  }
111
- super(errorMessage);
112
- if (errorObj.error) {
113
- this.name = errorObj.error;
114
- } else if (errorMessage && errorMessage.includes("stale element reference")) {
115
- this.name = "stale element reference";
116
- } else {
117
- this.name = errorObj.name || "WebDriver Error";
84
+ for (const [it, arg] of Object.entries(args)) {
85
+ if (isBidiCommand) {
86
+ break;
87
+ }
88
+ const i = parseInt(it, 10);
89
+ const commandParam = commandParams[i];
90
+ if (!isValidParameter(arg, commandParam.type)) {
91
+ if (typeof arg === "undefined" && !commandParam.required) {
92
+ continue;
93
+ }
94
+ const actual = commandParam.type.endsWith("[]") ? "(".concat((Array.isArray(arg) ? arg : [arg]).map((a) => getArgumentType(a)), ")[]") : getArgumentType(arg);
95
+ throw new Error(
96
+ 'Malformed type for "'.concat(commandParam.name, '" parameter of command ').concat(command, "\n") + "Expected: ".concat(commandParam.type, "\n") + "Actual: ".concat(actual) + moreInfo
97
+ );
98
+ }
99
+ if (i < variables.length) {
100
+ const encodedArg = doubleEncodeVariables ? encodeURIComponent(encodeURIComponent(arg)) : encodeURIComponent(arg);
101
+ endpoint = endpoint.replace(":".concat(commandParams[i].name), encodedArg);
102
+ continue;
103
+ }
104
+ body[commandParams[i].name] = arg;
118
105
  }
119
- Error.captureStackTrace(this, _WebDriverResponseError);
120
- this.url = url;
121
- this.opts = opts;
122
- this.message = this.computeErrorMessage();
106
+ const request = new environment.value.Request(method, endpoint, body, isHubCommand);
107
+ request.on("performance", (...args2) => this.emit("request.performance", ...args2));
108
+ this.emit("command", { command, method, endpoint, body });
109
+ log.info("COMMAND", commandCallStructure(command, args));
110
+ return request.makeRequest(this.options, this.sessionId).then((result) => {
111
+ var _a, _b;
112
+ if (typeof result.value !== "undefined") {
113
+ let resultLog = result.value;
114
+ if (/screenshot|recording/i.test(command) && typeof result.value === "string" && result.value.length > 64) {
115
+ resultLog = "".concat(result.value.slice(0, 61), "...");
116
+ } else if (command === "executeScript" && body.script && body.script.includes("(() => window.__wdioEvents__)")) {
117
+ resultLog = "[".concat(result.value.length, " framework events captured]");
118
+ }
119
+ log.info("RESULT", resultLog);
120
+ }
121
+ this.emit("result", { command, method, endpoint, body, result });
122
+ if (command === "deleteSession") {
123
+ const browser = this;
124
+ (_a = browser._bidiHandler) == null ? void 0 : _a.close();
125
+ const shutdownDriver = ((_b = body.deleteSessionOpts) == null ? void 0 : _b.shutdownDriver) !== false;
126
+ if (shutdownDriver && "wdio:driverPID" in this.capabilities && this.capabilities["wdio:driverPID"]) {
127
+ log.info("Kill driver process with PID ".concat(this.capabilities["wdio:driverPID"]));
128
+ try {
129
+ const killedSuccessfully = process.kill(this.capabilities["wdio:driverPID"], "SIGKILL");
130
+ if (!killedSuccessfully) {
131
+ log.warn("Failed to kill driver process, manually clean-up might be required");
132
+ }
133
+ } catch (err) {
134
+ log.warn("Failed to kill driver process", err);
135
+ }
136
+ setTimeout(() => {
137
+ for (const handle of process._getActiveHandles()) {
138
+ if (handle.servername && handle.servername.includes("edgedl.me")) {
139
+ handle.destroy();
140
+ }
141
+ }
142
+ }, 10);
143
+ }
144
+ if (!process.env.WDIO_WORKER_ID) {
145
+ logger.clearLogger();
146
+ }
147
+ }
148
+ return result.value;
149
+ });
150
+ };
151
+ }
152
+
153
+ // src/constants.ts
154
+ var DEFAULTS = {
155
+ /**
156
+ * protocol of automation driver
157
+ */
158
+ protocol: {
159
+ type: "string",
160
+ default: "http",
161
+ match: /(http|https)/
162
+ },
163
+ /**
164
+ * hostname of automation driver
165
+ */
166
+ hostname: {
167
+ type: "string",
168
+ default: "localhost"
169
+ },
170
+ /**
171
+ * port of automation driver
172
+ */
173
+ port: {
174
+ type: "number"
175
+ },
176
+ /**
177
+ * path to WebDriver endpoints
178
+ */
179
+ path: {
180
+ type: "string",
181
+ validate: (path2) => {
182
+ if (!path2.startsWith("/")) {
183
+ throw new TypeError('The option "path" needs to start with a "/"');
184
+ }
185
+ return true;
186
+ },
187
+ default: "/"
188
+ },
189
+ /**
190
+ * A key-value store of query parameters to be added to every selenium request
191
+ */
192
+ queryParams: {
193
+ type: "object"
194
+ },
195
+ /**
196
+ * cloud user if applicable
197
+ */
198
+ user: {
199
+ type: "string"
200
+ },
201
+ /**
202
+ * access key to user
203
+ */
204
+ key: {
205
+ type: "string"
206
+ },
207
+ /**
208
+ * capability of WebDriver session
209
+ */
210
+ capabilities: {
211
+ type: "object",
212
+ required: true
213
+ },
214
+ /**
215
+ * Level of logging verbosity
216
+ */
217
+ logLevel: {
218
+ type: "string",
219
+ default: "info",
220
+ match: /(trace|debug|info|warn|error|silent)/
221
+ },
222
+ /**
223
+ * directory for log files
224
+ */
225
+ outputDir: {
226
+ type: "string"
227
+ },
228
+ /**
229
+ * Timeout for any WebDriver request to a driver or grid
230
+ */
231
+ connectionRetryTimeout: {
232
+ type: "number",
233
+ default: 12e4
234
+ },
235
+ /**
236
+ * Count of request retries to the Selenium server
237
+ */
238
+ connectionRetryCount: {
239
+ type: "number",
240
+ default: 3
241
+ },
242
+ /**
243
+ * Override default agent
244
+ */
245
+ logLevels: {
246
+ type: "object"
247
+ },
248
+ /**
249
+ * Pass custom headers
250
+ */
251
+ headers: {
252
+ type: "object"
253
+ },
254
+ /**
255
+ * Function transforming the request options before the request is made
256
+ */
257
+ transformRequest: {
258
+ type: "function",
259
+ default: (requestOptions) => requestOptions
260
+ },
261
+ /**
262
+ * Function transforming the response object after it is received
263
+ */
264
+ transformResponse: {
265
+ type: "function",
266
+ default: (response) => response
267
+ },
268
+ /**
269
+ * Appium direct connect options server (https://appiumpro.com/editions/86-connecting-directly-to-appium-hosts-in-distributed-environments)
270
+ * Whether to allow direct connect caps to adjust endpoint details (Appium only)
271
+ */
272
+ enableDirectConnect: {
273
+ type: "boolean",
274
+ default: true
275
+ },
276
+ /**
277
+ * Whether it requires SSL certificates to be valid in HTTP/s requests
278
+ * for an environment which cannot get process environment well.
279
+ */
280
+ strictSSL: {
281
+ type: "boolean",
282
+ default: true
283
+ },
284
+ /**
285
+ * The path to the root of the cache directory. This directory is used to store all drivers that are downloaded
286
+ * when attempting to start a session.
287
+ */
288
+ cacheDir: {
289
+ type: "string",
290
+ default: environment.value.variables.WEBDRIVER_CACHE_DIR
123
291
  }
124
292
  };
293
+ var ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
294
+ var SHADOW_ELEMENT_KEY = "shadow-6066-11e4-a52e-4f735466cecf";
125
295
 
126
296
  // src/utils.ts
127
297
  import { deepmergeCustom } from "deepmerge-ts";
128
- import logger2 from "@wdio/logger";
298
+ import logger3 from "@wdio/logger";
129
299
  import {
130
300
  WebDriverProtocol,
131
301
  MJsonWProtocol,
@@ -134,71 +304,32 @@ import {
134
304
  SauceLabsProtocol,
135
305
  SeleniumProtocol,
136
306
  GeckoProtocol,
137
- WebDriverBidiProtocol
307
+ WebDriverBidiProtocol as WebDriverBidiProtocol2
138
308
  } from "@wdio/protocols";
139
309
  import { CAPABILITY_KEYS } from "@wdio/protocols";
140
310
 
141
311
  // src/bidi/core.ts
142
- import logger from "@wdio/logger";
143
-
144
- // src/bidi/socket.ts
145
- var BrowserSocket = class {
146
- #callbacks = /* @__PURE__ */ new Set();
147
- #ws;
148
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
149
- constructor(wsUrl, opts) {
150
- this.#ws = new globalThis.WebSocket(wsUrl);
151
- this.#ws.onmessage = this.handleMessage.bind(this);
152
- }
153
- handleMessage(event) {
154
- for (const callback of this.#callbacks) {
155
- callback(event.data);
156
- }
157
- }
158
- send(data) {
159
- this.#ws.send(data);
160
- }
161
- on(event, callback) {
162
- if (event === "open") {
163
- this.#ws.onopen = callback;
164
- } else if (event === "close") {
165
- this.#ws.onclose = callback;
166
- } else if (event === "error") {
167
- this.#ws.onerror = callback;
168
- } else {
169
- this.#callbacks.add(callback);
170
- }
171
- return this;
172
- }
173
- off(event, callback) {
174
- this.#callbacks.delete(callback);
175
- return this;
176
- }
177
- close() {
178
- this.#ws.close();
179
- }
180
- };
181
- var socket_default = globalThis.window ? BrowserSocket : (await import("ws")).default;
182
-
183
- // src/bidi/core.ts
184
- var log = logger("webdriver");
312
+ import logger2 from "@wdio/logger";
313
+ var log2 = logger2("webdriver");
185
314
  var RESPONSE_TIMEOUT = 1e3 * 60;
315
+ var _id, _ws, _waitForConnected, _webSocketUrl, _pendingCommands, _BidiCore_instances, handleResponse_fn;
186
316
  var BidiCore = class {
187
- #id = 0;
188
- #ws;
189
- #waitForConnected = Promise.resolve(false);
190
- #webSocketUrl;
191
- #pendingCommands = /* @__PURE__ */ new Map();
192
- client;
193
- /**
194
- * @private
195
- */
196
- _isConnected = false;
197
317
  constructor(webSocketUrl, opts) {
198
- this.#webSocketUrl = webSocketUrl;
199
- log.info(`Connect to webSocketUrl ${this.#webSocketUrl}`);
200
- this.#ws = new socket_default(this.#webSocketUrl, opts);
201
- this.#ws.on("message", this.#handleResponse.bind(this));
318
+ __privateAdd(this, _BidiCore_instances);
319
+ __privateAdd(this, _id, 0);
320
+ __privateAdd(this, _ws);
321
+ __privateAdd(this, _waitForConnected, Promise.resolve(false));
322
+ __privateAdd(this, _webSocketUrl);
323
+ __privateAdd(this, _pendingCommands, /* @__PURE__ */ new Map());
324
+ __publicField(this, "client");
325
+ /**
326
+ * @private
327
+ */
328
+ __publicField(this, "_isConnected", false);
329
+ __privateSet(this, _webSocketUrl, webSocketUrl);
330
+ log2.info("Connect to webSocketUrl ".concat(__privateGet(this, _webSocketUrl)));
331
+ __privateSet(this, _ws, new environment.value.Socket(__privateGet(this, _webSocketUrl), opts));
332
+ __privateGet(this, _ws).on("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
202
333
  }
203
334
  /**
204
335
  * We initiate the Bidi instance before a WebdriverIO instance is created.
@@ -213,36 +344,36 @@ var BidiCore = class {
213
344
  this._isConnected = true;
214
345
  return;
215
346
  }
216
- this.#waitForConnected = new Promise((resolve) => {
217
- this.#ws.on("open", () => {
218
- log.info("Connected session to Bidi protocol");
347
+ __privateSet(this, _waitForConnected, new Promise((resolve) => {
348
+ __privateGet(this, _ws).on("open", () => {
349
+ log2.info("Connected session to Bidi protocol");
219
350
  this._isConnected = true;
220
351
  resolve(this._isConnected);
221
352
  });
222
- this.#ws.on("error", (err) => {
223
- log.warn(`Couldn't connect to Bidi protocol: ${err.message}`);
353
+ __privateGet(this, _ws).on("error", (err) => {
354
+ log2.warn("Couldn't connect to Bidi protocol: ".concat(err.message));
224
355
  this._isConnected = false;
225
356
  resolve(this._isConnected);
226
357
  });
227
- });
228
- return this.#waitForConnected;
358
+ }));
359
+ return __privateGet(this, _waitForConnected);
229
360
  }
230
361
  close() {
231
362
  if (!this._isConnected) {
232
363
  return;
233
364
  }
234
- log.info(`Close Bidi connection to ${this.#webSocketUrl}`);
365
+ log2.info("Close Bidi connection to ".concat(__privateGet(this, _webSocketUrl)));
235
366
  this._isConnected = false;
236
- this.#ws.off("message", this.#handleResponse.bind(this));
237
- this.#ws.close();
238
- this.#ws.terminate();
367
+ __privateGet(this, _ws).off("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
368
+ __privateGet(this, _ws).close();
369
+ __privateGet(this, _ws).terminate();
239
370
  }
240
371
  reconnect(webSocketUrl, opts) {
241
- log.info(`Reconnect to new Bidi session at ${webSocketUrl}`);
372
+ log2.info("Reconnect to new Bidi session at ".concat(webSocketUrl));
242
373
  this.close();
243
- this.#webSocketUrl = webSocketUrl;
244
- this.#ws = new socket_default(this.#webSocketUrl, opts);
245
- this.#ws.on("message", this.#handleResponse.bind(this));
374
+ __privateSet(this, _webSocketUrl, webSocketUrl);
375
+ __privateSet(this, _ws, new environment.value.Socket(__privateGet(this, _webSocketUrl), opts));
376
+ __privateGet(this, _ws).on("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
246
377
  return this.connect();
247
378
  }
248
379
  /**
@@ -250,10 +381,10 @@ var BidiCore = class {
250
381
  * @returns a promise that resolves once the connection to WebDriver Bidi protocol was established
251
382
  */
252
383
  waitForConnected() {
253
- return this.#waitForConnected;
384
+ return __privateGet(this, _waitForConnected);
254
385
  }
255
386
  get socket() {
256
- return this.#ws;
387
+ return __privateGet(this, _ws);
257
388
  }
258
389
  get isConnected() {
259
390
  return this._isConnected;
@@ -263,76 +394,81 @@ var BidiCore = class {
263
394
  * @internal
264
395
  */
265
396
  get __handleResponse() {
266
- return this.#handleResponse.bind(this);
267
- }
268
- #handleResponse(data) {
269
- try {
270
- const payload = JSON.parse(data.toString());
271
- if (!payload.id) {
272
- return;
273
- }
274
- log.info("BIDI RESULT", data.toString());
275
- this.client?.emit("bidiResult", payload);
276
- const resolve = this.#pendingCommands.get(payload.id);
277
- if (!resolve) {
278
- log.error(`Couldn't resolve command with id ${payload.id}`);
279
- return;
280
- }
281
- this.#pendingCommands.delete(payload.id);
282
- resolve(payload);
283
- } catch (err) {
284
- const error = err instanceof Error ? err : new Error(`Failed parse message: ${String(err)}`);
285
- log.error(`Failed parse message: ${error.message}`);
286
- }
397
+ return __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this);
287
398
  }
288
399
  async send(params) {
289
400
  const id = this.sendAsync(params);
290
- const failError = new Error(`WebDriver Bidi command "${params.method}" failed`);
401
+ const failError = new Error('WebDriver Bidi command "'.concat(params.method, '" failed'));
291
402
  const payload = await new Promise((resolve, reject) => {
292
403
  const t = setTimeout(() => {
293
- reject(new Error(`Command ${params.method} with id ${id} (with the following parameter: ${JSON.stringify(params.params)}) timed out`));
294
- this.#pendingCommands.delete(id);
404
+ reject(new Error("Command ".concat(params.method, " with id ").concat(id, " (with the following parameter: ").concat(JSON.stringify(params.params), ") timed out")));
405
+ __privateGet(this, _pendingCommands).delete(id);
295
406
  }, RESPONSE_TIMEOUT);
296
- this.#pendingCommands.set(id, (payload2) => {
407
+ __privateGet(this, _pendingCommands).set(id, (payload2) => {
297
408
  clearTimeout(t);
298
409
  resolve(payload2);
299
410
  });
300
411
  });
301
412
  if (payload.error) {
302
- failError.message += ` with error: ${payload.error} - ${payload.message}`;
413
+ failError.message += " with error: ".concat(payload.error, " - ").concat(payload.message);
303
414
  if (payload.stacktrace) {
304
- const driverStack = payload.stacktrace.split("\n").filter(Boolean).map((line) => ` at ${line}`).join("\n");
305
- failError.stack += `
306
-
307
- Driver Stack:
308
- ${driverStack}`;
415
+ const driverStack = payload.stacktrace.split("\n").filter(Boolean).map((line) => " at ".concat(line)).join("\n");
416
+ failError.stack += "\n\nDriver Stack:\n".concat(driverStack);
309
417
  }
310
418
  throw failError;
311
419
  }
312
420
  return payload;
313
421
  }
314
422
  sendAsync(params) {
423
+ var _a;
315
424
  if (!this._isConnected) {
316
425
  throw new Error("No connection to WebDriver Bidi was established");
317
426
  }
318
- log.info("BIDI COMMAND", ...parseBidiCommand(params));
319
- const id = ++this.#id;
320
- this.client?.emit("bidiCommand", params);
321
- this.#ws.send(JSON.stringify({ id, ...params }));
427
+ log2.info("BIDI COMMAND", ...parseBidiCommand(params));
428
+ const id = ++__privateWrapper(this, _id)._;
429
+ (_a = this.client) == null ? void 0 : _a.emit("bidiCommand", params);
430
+ __privateGet(this, _ws).send(JSON.stringify({ id, ...params }));
322
431
  return id;
323
432
  }
324
433
  };
434
+ _id = new WeakMap();
435
+ _ws = new WeakMap();
436
+ _waitForConnected = new WeakMap();
437
+ _webSocketUrl = new WeakMap();
438
+ _pendingCommands = new WeakMap();
439
+ _BidiCore_instances = new WeakSet();
440
+ handleResponse_fn = function(data) {
441
+ var _a;
442
+ try {
443
+ const payload = JSON.parse(data.toString());
444
+ if (!payload.id) {
445
+ return;
446
+ }
447
+ log2.info("BIDI RESULT", data.toString());
448
+ (_a = this.client) == null ? void 0 : _a.emit("bidiResult", payload);
449
+ const resolve = __privateGet(this, _pendingCommands).get(payload.id);
450
+ if (!resolve) {
451
+ log2.error("Couldn't resolve command with id ".concat(payload.id));
452
+ return;
453
+ }
454
+ __privateGet(this, _pendingCommands).delete(payload.id);
455
+ resolve(payload);
456
+ } catch (err) {
457
+ const error = err instanceof Error ? err : new Error("Failed parse message: ".concat(String(err)));
458
+ log2.error("Failed parse message: ".concat(error.message));
459
+ }
460
+ };
325
461
  function parseBidiCommand(params) {
326
462
  const commandName = params.method;
327
463
  if (commandName === "script.addPreloadScript") {
328
464
  const param = params.params;
329
- const logString = `{ functionDeclaration: <PreloadScript[${new TextEncoder().encode(param.functionDeclaration).length} bytes]>, contexts: ${JSON.stringify(param.contexts)} }`;
465
+ const logString = "{ functionDeclaration: <PreloadScript[".concat(new TextEncoder().encode(param.functionDeclaration).length, " bytes]>, contexts: ").concat(JSON.stringify(param.contexts), " }");
330
466
  return [commandName, logString];
331
467
  } else if (commandName === "script.callFunction") {
332
468
  const param = params.params;
333
469
  const logString = JSON.stringify({
334
470
  ...param,
335
- functionDeclaration: `<Function[${new TextEncoder().encode(param.functionDeclaration).length} bytes]>`
471
+ functionDeclaration: "<Function[".concat(new TextEncoder().encode(param.functionDeclaration).length, " bytes]>")
336
472
  });
337
473
  return [commandName, logString];
338
474
  }
@@ -864,7 +1000,7 @@ var BidiHandler = class extends BidiCore {
864
1000
  };
865
1001
 
866
1002
  // src/utils.ts
867
- var log2 = logger2("webdriver");
1003
+ var log3 = logger3("webdriver");
868
1004
  var deepmerge = deepmergeCustom({ mergeArrays: false });
869
1005
  var BROWSER_DRIVER_ERRORS = [
870
1006
  "unknown command: wd/hub/session",
@@ -891,7 +1027,7 @@ async function startWebDriverSession(params) {
891
1027
  capabilities.alwaysMatch.unhandledPromptBehavior = "ignore";
892
1028
  }
893
1029
  validateCapabilities(capabilities.alwaysMatch);
894
- const sessionRequest = new FetchRequest(
1030
+ const sessionRequest = new environment.value.Request(
895
1031
  "POST",
896
1032
  "/session",
897
1033
  { capabilities }
@@ -900,7 +1036,7 @@ async function startWebDriverSession(params) {
900
1036
  try {
901
1037
  response = await sessionRequest.makeRequest(params);
902
1038
  } catch (err) {
903
- log2.error(err);
1039
+ log3.error(err);
904
1040
  const message = getSessionError(err, params);
905
1041
  throw new Error("Failed to create session.\n" + message);
906
1042
  }
@@ -909,7 +1045,8 @@ async function startWebDriverSession(params) {
909
1045
  return { sessionId, capabilities: params.capabilities };
910
1046
  }
911
1047
  function validateCapabilities(capabilities) {
912
- const chromeArgs = capabilities["goog:chromeOptions"]?.args || [];
1048
+ var _a;
1049
+ const chromeArgs = ((_a = capabilities["goog:chromeOptions"]) == null ? void 0 : _a.args) || [];
913
1050
  if (chromeArgs.includes("incognito") || chromeArgs.includes("--incognito")) {
914
1051
  throw new Error(
915
1052
  'Please remove "incognito" from `"goog:chromeOptions".args` as it is not supported running Chrome with WebDriver. WebDriver sessions are always incognito mode and do not persist across browser sessions.'
@@ -920,14 +1057,14 @@ function validateCapabilities(capabilities) {
920
1057
  const invalidWebDriverCaps = Object.keys(capabilities).filter((cap) => !CAPABILITY_KEYS.includes(cap) && !cap.includes(":"));
921
1058
  if (extensionCaps.length && invalidWebDriverCaps.length) {
922
1059
  throw new Error(
923
- `Invalid or unsupported WebDriver capabilities found ("${invalidWebDriverCaps.join('", "')}"). Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to your vendor support team if you have further questions.`
1060
+ 'Invalid or unsupported WebDriver capabilities found ("'.concat(invalidWebDriverCaps.join('", "'), '"). ') + 'Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to your vendor support team if you have further questions.'
924
1061
  );
925
1062
  }
926
1063
  }
927
1064
  }
928
1065
  function isSuccessfulResponse(statusCode, body) {
929
1066
  if (!body || typeof body.value === "undefined") {
930
- log2.debug("request failed due to missing body");
1067
+ log3.debug("request failed due to missing body");
931
1068
  return false;
932
1069
  }
933
1070
  if (body.status === 7 && body.value && body.value.message && (body.value.message.toLowerCase().startsWith("no such element") || // Appium
@@ -936,7 +1073,7 @@ function isSuccessfulResponse(statusCode, body) {
936
1073
  return true;
937
1074
  }
938
1075
  if (body.status && body.status !== 0) {
939
- log2.debug(`request failed due to status ${body.status}`);
1076
+ log3.debug("request failed due to status ".concat(body.status));
940
1077
  return false;
941
1078
  }
942
1079
  const hasErrorResponse = body.value && (body.value.error || body.value.stackTrace || body.value.stacktrace);
@@ -947,7 +1084,7 @@ function isSuccessfulResponse(statusCode, body) {
947
1084
  return true;
948
1085
  }
949
1086
  if (hasErrorResponse) {
950
- log2.debug("request failed due to response error:", body.value.error);
1087
+ log3.debug("request failed due to response error:", body.value.error);
951
1088
  return false;
952
1089
  }
953
1090
  return true;
@@ -964,7 +1101,7 @@ function getPrototype({ isW3C, isChromium, isFirefox, isMobile, isSauce, isSelen
964
1101
  /**
965
1102
  * enable Bidi protocol for W3C sessions
966
1103
  */
967
- isW3C ? WebDriverBidiProtocol : {},
1104
+ isW3C ? WebDriverBidiProtocol2 : {},
968
1105
  /**
969
1106
  * only apply mobile protocol if session is actually for mobile
970
1107
  */
@@ -1011,7 +1148,8 @@ function getEnvironmentVars({ isW3C, isMobile, isIOS, isAndroid, isFirefox, isSa
1011
1148
  * by the session response.
1012
1149
  */
1013
1150
  get: function() {
1014
- return Boolean(this._bidiHandler?.isConnected);
1151
+ var _a;
1152
+ return Boolean((_a = this._bidiHandler) == null ? void 0 : _a.isConnected);
1015
1153
  }
1016
1154
  },
1017
1155
  isChromium: { value: isChromium }
@@ -1024,7 +1162,7 @@ function setupDirectConnect(client) {
1024
1162
  const directConnectPath = capabilities["appium:directConnectPath"];
1025
1163
  const directConnectPort = capabilities["appium:directConnectPort"];
1026
1164
  if (directConnectProtocol && directConnectHost && directConnectPort && (directConnectPath || directConnectPath === "")) {
1027
- log2.info(`Found direct connect information in new session response. Will connect to server at ${directConnectProtocol}://${directConnectHost}:${directConnectPort}${directConnectPath}`);
1165
+ log3.info("Found direct connect information in new session response. " + "Will connect to server at ".concat(directConnectProtocol, "://") + "".concat(directConnectHost, ":").concat(directConnectPort).concat(directConnectPath));
1028
1166
  client.options.protocol = directConnectProtocol;
1029
1167
  client.options.hostname = directConnectHost;
1030
1168
  client.options.port = directConnectPort;
@@ -1032,228 +1170,380 @@ function setupDirectConnect(client) {
1032
1170
  }
1033
1171
  }
1034
1172
  var getSessionError = (err, params = {}) => {
1173
+ var _a;
1035
1174
  if (err.code === "ECONNREFUSED") {
1036
- return `Unable to connect to "${params.protocol}://${params.hostname}:${params.port}${params.path}", make sure browser driver is running on that address.
1037
- It seems like the service failed to start or is rejecting any connections.`;
1175
+ return 'Unable to connect to "'.concat(params.protocol, "://").concat(params.hostname, ":").concat(params.port).concat(params.path, '", make sure browser driver is running on that address.') + "\nIt seems like the service failed to start or is rejecting any connections.";
1038
1176
  }
1039
1177
  if (err.message === "unhandled request") {
1040
- return `The browser driver couldn't start the session. Make sure you have set the "path" correctly!`;
1178
+ return 'The browser driver couldn\'t start the session. Make sure you have set the "path" correctly!';
1041
1179
  }
1042
1180
  if (!err.message) {
1043
1181
  return "See wdio.* logs for more information.";
1044
1182
  }
1045
- if (err.message.includes("Whoops! The URL specified routes to this help page.")) {
1046
- return "It seems you are running a Selenium Standalone server and point to a wrong path. Please set `path: '/wd/hub'` in your wdio.conf.js!";
1183
+ if (err.message.includes("Whoops! The URL specified routes to this help page.")) {
1184
+ return "It seems you are running a Selenium Standalone server and point to a wrong path. Please set `path: '/wd/hub'` in your wdio.conf.js!";
1185
+ }
1186
+ if (BROWSER_DRIVER_ERRORS.some((m) => err && err.message && err.message.includes(m))) {
1187
+ return "Make sure to set `path: '/'` in your wdio.conf.js!";
1188
+ }
1189
+ if (err.message.includes("Bad Request - Invalid Hostname") && err.message.includes("HTTP Error 400")) {
1190
+ return "Run edge driver on 127.0.0.1 instead of localhost, ex: --host=127.0.0.1, or set `hostname: 'localhost'` in your wdio.conf.js";
1191
+ }
1192
+ const w3cCapMessage = '\nMake sure to add vendor prefix like "goog:", "appium:", "moz:", etc to non W3C capabilities.\nSee more https://www.w3.org/TR/webdriver/#capabilities';
1193
+ if (err.message.includes("Illegal key values seen in w3c capabilities")) {
1194
+ return err.message + w3cCapMessage;
1195
+ }
1196
+ if (err.message === "Response has empty body") {
1197
+ return "Make sure to connect to valid hostname:port or the port is not in use.\nIf you use a grid server " + w3cCapMessage;
1198
+ }
1199
+ if (err.message.includes("failed serving request POST /wd/hub/session: Unauthorized") && ((_a = params.hostname) == null ? void 0 : _a.endsWith("saucelabs.com"))) {
1200
+ return "Session request was not authorized because you either did provide a wrong access key or tried to run in a region that has not been enabled for your user. If have registered a free trial account it is connected to a specific region. Ensure this region is set in your configuration (https://webdriver.io/docs/options.html#region).";
1201
+ }
1202
+ return err.message;
1203
+ };
1204
+ function initiateBidi(socketUrl, strictSSL = true) {
1205
+ socketUrl = socketUrl.replace("localhost", "127.0.0.1");
1206
+ const bidiReqOpts = strictSSL ? {} : { rejectUnauthorized: false };
1207
+ const handler = new BidiHandler(socketUrl, bidiReqOpts);
1208
+ handler.connect().then((isConnected) => isConnected && log3.info("Connected to WebDriver Bidi interface at ".concat(socketUrl)));
1209
+ return {
1210
+ _bidiHandler: { value: handler },
1211
+ ...Object.values(WebDriverBidiProtocol2).map((def) => def.socket).reduce((acc, cur) => {
1212
+ acc[cur.command] = {
1213
+ value: function(...args) {
1214
+ const bidiFn = handler[cur.command];
1215
+ handler.attachClient(this);
1216
+ this.emit(cur.command, args);
1217
+ return bidiFn == null ? void 0 : bidiFn.apply(handler, args);
1218
+ }
1219
+ };
1220
+ return acc;
1221
+ }, {})
1222
+ };
1223
+ }
1224
+ function parseBidiMessage(data) {
1225
+ try {
1226
+ const payload = JSON.parse(data.toString());
1227
+ if (payload.type !== "event") {
1228
+ return;
1229
+ }
1230
+ this.emit(payload.method, payload.params);
1231
+ } catch (err) {
1232
+ log3.error("Failed parse WebDriver Bidi message: ".concat(err.message));
1233
+ }
1234
+ }
1235
+
1236
+ // src/bidi/localTypes.ts
1237
+ var localTypes_exports = {};
1238
+
1239
+ // src/bidi/remoteTypes.ts
1240
+ var remoteTypes_exports = {};
1241
+
1242
+ // src/index.ts
1243
+ var log4 = logger4("webdriver");
1244
+ var WebDriver = class _WebDriver {
1245
+ static async newSession(options, modifier, userPrototype = {}, customCommandWrapper) {
1246
+ const envLogLevel = process.env.WDIO_LOG_LEVEL;
1247
+ options.logLevel = envLogLevel != null ? envLogLevel : options.logLevel;
1248
+ const params = validateConfig(DEFAULTS, options);
1249
+ if (params.logLevel && (!options.logLevels || !options.logLevels.webdriver)) {
1250
+ logger4.setLevel("webdriver", params.logLevel);
1251
+ }
1252
+ log4.info("Initiate new session using the WebDriver protocol");
1253
+ const driverProcess = await startWebDriver(params);
1254
+ const requestedCapabilities = { ...params.capabilities };
1255
+ const { sessionId, capabilities } = await startWebDriverSession(params);
1256
+ const environment2 = sessionEnvironmentDetector({ capabilities, requestedCapabilities });
1257
+ const environmentPrototype = getEnvironmentVars(environment2);
1258
+ const protocolCommands = getPrototype(environment2);
1259
+ if (driverProcess == null ? void 0 : driverProcess.pid) {
1260
+ capabilities["wdio:driverPID"] = driverProcess.pid;
1261
+ }
1262
+ const bidiPrototype = {};
1263
+ if (isBidi(requestedCapabilities, capabilities)) {
1264
+ log4.info("Register BiDi handler for session with id ".concat(sessionId));
1265
+ Object.assign(bidiPrototype, initiateBidi(capabilities.webSocketUrl, options.strictSSL));
1266
+ }
1267
+ const monad = webdriverMonad(
1268
+ { ...params, requestedCapabilities },
1269
+ modifier,
1270
+ {
1271
+ ...protocolCommands,
1272
+ ...environmentPrototype,
1273
+ ...userPrototype,
1274
+ ...bidiPrototype
1275
+ }
1276
+ );
1277
+ const client = monad(sessionId, customCommandWrapper);
1278
+ if (isBidi(requestedCapabilities, capabilities)) {
1279
+ await client._bidiHandler.waitForConnected();
1280
+ client._bidiHandler.socket.on("message", parseBidiMessage.bind(client));
1281
+ }
1282
+ if (params.enableDirectConnect) {
1283
+ setupDirectConnect(client);
1284
+ }
1285
+ return client;
1286
+ }
1287
+ /**
1288
+ * allows user to attach to existing sessions
1289
+ */
1290
+ static attachToSession(options, modifier, userPrototype = {}, commandWrapper) {
1291
+ var _a, _b;
1292
+ if (!options || typeof options.sessionId !== "string") {
1293
+ throw new Error("sessionId is required to attach to existing session");
1294
+ }
1295
+ if (options.logLevel) {
1296
+ logger4.setLevel("webdriver", options.logLevel);
1297
+ }
1298
+ options.capabilities = options.capabilities || {};
1299
+ options.isW3C = options.isW3C === false ? false : true;
1300
+ options.protocol = options.protocol || DEFAULTS.protocol.default;
1301
+ options.hostname = options.hostname || DEFAULTS.hostname.default;
1302
+ options.port = options.port || DEFAULTS.port.default;
1303
+ options.path = options.path || DEFAULTS.path.default;
1304
+ const environment2 = sessionEnvironmentDetector({ capabilities: options.capabilities, requestedCapabilities: options.capabilities });
1305
+ options = Object.assign(environment2, options);
1306
+ const environmentPrototype = getEnvironmentVars(options);
1307
+ const protocolCommands = getPrototype(options);
1308
+ const bidiPrototype = {};
1309
+ if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
1310
+ const webSocketUrl = (_a = options.capabilities) == null ? void 0 : _a.webSocketUrl;
1311
+ log4.info("Register BiDi handler for session with id ".concat(options.sessionId));
1312
+ Object.assign(bidiPrototype, initiateBidi(webSocketUrl, options.strictSSL));
1313
+ }
1314
+ const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype };
1315
+ const monad = webdriverMonad(options, modifier, prototype);
1316
+ const client = monad(options.sessionId, commandWrapper);
1317
+ if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
1318
+ (_b = client._bidiHandler) == null ? void 0 : _b.socket.on("message", parseBidiMessage.bind(client));
1319
+ }
1320
+ return client;
1321
+ }
1322
+ /**
1323
+ * Changes The instance session id and browser capabilities for the new session
1324
+ * directly into the passed in browser object
1325
+ *
1326
+ * @param {object} instance the object we get from a new browser session.
1327
+ * @returns {string} the new session id of the browser
1328
+ */
1329
+ static async reloadSession(instance, newCapabilities) {
1330
+ var _a;
1331
+ const capabilities = newCapabilities ? newCapabilities : Object.assign({}, instance.requestedCapabilities);
1332
+ let params = { ...instance.options, capabilities };
1333
+ for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
1334
+ if (prop in capabilities) {
1335
+ params = { ...params, [prop]: capabilities[prop] };
1336
+ delete capabilities[prop];
1337
+ }
1338
+ }
1339
+ let driverProcess;
1340
+ if (params.hostname === "localhost" && (newCapabilities == null ? void 0 : newCapabilities.browserName)) {
1341
+ delete params.port;
1342
+ delete params.hostname;
1343
+ driverProcess = await startWebDriver(params);
1344
+ }
1345
+ const { sessionId, capabilities: newSessionCapabilities } = await startWebDriverSession(params);
1346
+ if (driverProcess == null ? void 0 : driverProcess.pid) {
1347
+ newSessionCapabilities["wdio:driverPID"] = driverProcess.pid;
1348
+ }
1349
+ for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
1350
+ if (prop in params) {
1351
+ instance.options[prop] = params[prop];
1352
+ }
1353
+ }
1354
+ for (const prop in instance.requestedCapabilities) {
1355
+ delete instance.requestedCapabilities[prop];
1356
+ }
1357
+ const driverPid = instance.capabilities["wdio:driverPID"];
1358
+ instance.sessionId = sessionId;
1359
+ instance.capabilities = newSessionCapabilities;
1360
+ instance.capabilities["wdio:driverPID"] = driverPid;
1361
+ Object.assign(instance.requestedCapabilities, capabilities);
1362
+ if (isBidi(instance.requestedCapabilities || {}, instance.capabilities || {})) {
1363
+ const bidiReqOpts = instance.options.strictSSL ? {} : { rejectUnauthorized: false };
1364
+ await ((_a = instance._bidiHandler) == null ? void 0 : _a.reconnect(newSessionCapabilities.webSocketUrl, bidiReqOpts));
1365
+ }
1366
+ return sessionId;
1367
+ }
1368
+ static get WebDriver() {
1369
+ return _WebDriver;
1370
+ }
1371
+ };
1372
+
1373
+ // src/bidi/socket.ts
1374
+ var _callbacks, _ws2;
1375
+ var BrowserSocket = class {
1376
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1377
+ constructor(wsUrl, opts) {
1378
+ __privateAdd(this, _callbacks, /* @__PURE__ */ new Set());
1379
+ __privateAdd(this, _ws2);
1380
+ __privateSet(this, _ws2, new WebSocket(wsUrl));
1381
+ __privateGet(this, _ws2).onmessage = this.handleMessage.bind(this);
1382
+ }
1383
+ handleMessage(event) {
1384
+ for (const callback of __privateGet(this, _callbacks)) {
1385
+ callback(event.data);
1386
+ }
1387
+ }
1388
+ send(data) {
1389
+ __privateGet(this, _ws2).send(data);
1390
+ }
1391
+ on(event, callback) {
1392
+ if (event === "open") {
1393
+ __privateGet(this, _ws2).onopen = callback;
1394
+ } else if (event === "close") {
1395
+ __privateGet(this, _ws2).onclose = callback;
1396
+ } else if (event === "error") {
1397
+ __privateGet(this, _ws2).onerror = callback;
1398
+ } else {
1399
+ __privateGet(this, _callbacks).add(callback);
1400
+ }
1401
+ return this;
1402
+ }
1403
+ off(event, callback) {
1404
+ __privateGet(this, _callbacks).delete(callback);
1405
+ return this;
1406
+ }
1407
+ close() {
1408
+ __privateGet(this, _ws2).close();
1047
1409
  }
1048
- if (BROWSER_DRIVER_ERRORS.some((m) => err && err.message && err.message.includes(m))) {
1049
- return "Make sure to set `path: '/'` in your wdio.conf.js!";
1410
+ };
1411
+ _callbacks = new WeakMap();
1412
+ _ws2 = new WeakMap();
1413
+
1414
+ // src/request/request.ts
1415
+ import path from "node:path";
1416
+ import { EventEmitter } from "node:events";
1417
+ import logger5 from "@wdio/logger";
1418
+ import { transformCommandLogResult as transformCommandLogResult2, sleep } from "@wdio/utils";
1419
+
1420
+ // src/request/error.ts
1421
+ import { transformCommandLogResult } from "@wdio/utils";
1422
+
1423
+ // src/request/constants.ts
1424
+ var RETRYABLE_STATUS_CODES = [408, 413, 429, 500, 502, 503, 504];
1425
+ var RETRYABLE_ERROR_CODES = [
1426
+ "ETIMEDOUT",
1427
+ "ECONNRESET",
1428
+ "EADDRINUSE",
1429
+ "ECONNREFUSED",
1430
+ "EPIPE",
1431
+ "ENOTFOUND",
1432
+ "ENETUNREACH",
1433
+ "EAI_AGAIN",
1434
+ // additional error codes we like to retry
1435
+ "UND_ERR_CONNECT_TIMEOUT",
1436
+ "UND_ERR_SOCKET"
1437
+ ];
1438
+ var REG_EXPS = {
1439
+ commandName: /.*\/session\/[0-9a-f-]+\/(.*)/,
1440
+ execFn: /return \(([\s\S]*)\)\.apply\(null, arguments\)/
1441
+ };
1442
+
1443
+ // src/request/error.ts
1444
+ var _WebDriverError_instances, getExecCmdName_fn, getExecCmdArgs_fn;
1445
+ var WebDriverError = class extends Error {
1446
+ constructor() {
1447
+ super(...arguments);
1448
+ __privateAdd(this, _WebDriverError_instances);
1050
1449
  }
1051
- if (err.message.includes("Bad Request - Invalid Hostname") && err.message.includes("HTTP Error 400")) {
1052
- return "Run edge driver on 127.0.0.1 instead of localhost, ex: --host=127.0.0.1, or set `hostname: 'localhost'` in your wdio.conf.js";
1450
+ /**
1451
+ * return timeout error with information about the executing command on which the test hangs
1452
+ */
1453
+ computeErrorMessage() {
1454
+ const cmdName = __privateMethod(this, _WebDriverError_instances, getExecCmdName_fn).call(this);
1455
+ const cmdArgs = __privateMethod(this, _WebDriverError_instances, getExecCmdArgs_fn).call(this, this.opts);
1456
+ const cmdInfoMsg = 'when running "'.concat(cmdName, '" with method "').concat(this.opts.method, '"');
1457
+ const cmdArgsMsg = cmdArgs ? " and args ".concat(cmdArgs) : "";
1458
+ return "WebDriverError: ".concat(this.message, " ").concat(cmdInfoMsg).concat(cmdArgsMsg);
1053
1459
  }
1054
- const w3cCapMessage = '\nMake sure to add vendor prefix like "goog:", "appium:", "moz:", etc to non W3C capabilities.\nSee more https://www.w3.org/TR/webdriver/#capabilities';
1055
- if (err.message.includes("Illegal key values seen in w3c capabilities")) {
1056
- return err.message + w3cCapMessage;
1460
+ };
1461
+ _WebDriverError_instances = new WeakSet();
1462
+ getExecCmdName_fn = function() {
1463
+ const { href } = this.url;
1464
+ const res = href.match(REG_EXPS.commandName) || [];
1465
+ return res[1] || href;
1466
+ };
1467
+ getExecCmdArgs_fn = function(requestOptions) {
1468
+ const { body: cmdJson } = requestOptions;
1469
+ if (typeof cmdJson !== "object") {
1470
+ return "";
1057
1471
  }
1058
- if (err.message === "Response has empty body") {
1059
- return "Make sure to connect to valid hostname:port or the port is not in use.\nIf you use a grid server " + w3cCapMessage;
1472
+ const transformedRes = transformCommandLogResult(cmdJson);
1473
+ if (typeof transformedRes === "string") {
1474
+ return transformedRes;
1060
1475
  }
1061
- if (err.message.includes("failed serving request POST /wd/hub/session: Unauthorized") && params.hostname?.endsWith("saucelabs.com")) {
1062
- return "Session request was not authorized because you either did provide a wrong access key or tried to run in a region that has not been enabled for your user. If have registered a free trial account it is connected to a specific region. Ensure this region is set in your configuration (https://webdriver.io/docs/options.html#region).";
1476
+ if (typeof cmdJson.script === "string") {
1477
+ const scriptRes = cmdJson.script.match(REG_EXPS.execFn) || [];
1478
+ return '"'.concat(scriptRes[1] || cmdJson.script, '"');
1063
1479
  }
1064
- return err.message;
1480
+ return Object.keys(cmdJson).length ? '"'.concat(JSON.stringify(cmdJson), '"') : "";
1065
1481
  };
1066
- function initiateBidi(socketUrl, strictSSL = true) {
1067
- socketUrl = socketUrl.replace("localhost", "127.0.0.1");
1068
- const bidiReqOpts = strictSSL ? {} : { rejectUnauthorized: false };
1069
- const handler = new BidiHandler(socketUrl, bidiReqOpts);
1070
- handler.connect().then((isConnected) => isConnected && log2.info(`Connected to WebDriver Bidi interface at ${socketUrl}`));
1071
- return {
1072
- _bidiHandler: { value: handler },
1073
- ...Object.values(WebDriverBidiProtocol).map((def) => def.socket).reduce((acc, cur) => {
1074
- acc[cur.command] = {
1075
- value: function(...args) {
1076
- const bidiFn = handler[cur.command];
1077
- handler.attachClient(this);
1078
- this.emit(cur.command, args);
1079
- return bidiFn?.apply(handler, args);
1080
- }
1081
- };
1082
- return acc;
1083
- }, {})
1084
- };
1085
- }
1086
- function parseBidiMessage(data) {
1087
- try {
1088
- const payload = JSON.parse(data.toString());
1089
- if (payload.type !== "event") {
1090
- return;
1482
+ var WebDriverRequestError = class extends WebDriverError {
1483
+ constructor(err, url, opts) {
1484
+ let message = err.message;
1485
+ if (err.message === "fetch failed") {
1486
+ message = "Failed to fetch [".concat(opts.method, "] ").concat(url.href, ": please make sure you have a WebDriver compatible server running on ").concat(url.origin);
1091
1487
  }
1092
- this.emit(payload.method, payload.params);
1093
- } catch (err) {
1094
- log2.error(`Failed parse WebDriver Bidi message: ${err.message}`);
1488
+ super(message);
1489
+ __publicField(this, "url");
1490
+ __publicField(this, "opts");
1491
+ __publicField(this, "statusCode");
1492
+ __publicField(this, "body");
1493
+ __publicField(this, "code");
1494
+ this.url = url;
1495
+ this.opts = opts;
1496
+ const errorCode = typeof err.cause === "object" && err.cause && "code" in err.cause && typeof err.cause.code === "string" ? err.cause.code : "code" in err && typeof err.code === "string" ? err.code : void 0;
1497
+ if (errorCode) {
1498
+ this.code = errorCode;
1499
+ this.message = errorCode === "UND_ERR_CONNECT_TIMEOUT" ? 'Request timed out! Consider increasing the "connectionRetryTimeout" option.' : "Request failed with error code " + errorCode;
1500
+ }
1501
+ this.message = this.computeErrorMessage();
1095
1502
  }
1096
- }
1097
-
1098
- // src/constants.ts
1099
- import os from "node:os";
1100
- var DEFAULTS = {
1101
- /**
1102
- * protocol of automation driver
1103
- */
1104
- protocol: {
1105
- type: "string",
1106
- default: "http",
1107
- match: /(http|https)/
1108
- },
1109
- /**
1110
- * hostname of automation driver
1111
- */
1112
- hostname: {
1113
- type: "string",
1114
- default: "localhost"
1115
- },
1116
- /**
1117
- * port of automation driver
1118
- */
1119
- port: {
1120
- type: "number"
1121
- },
1122
- /**
1123
- * path to WebDriver endpoints
1124
- */
1125
- path: {
1126
- type: "string",
1127
- validate: (path2) => {
1128
- if (!path2.startsWith("/")) {
1129
- throw new TypeError('The option "path" needs to start with a "/"');
1130
- }
1131
- return true;
1132
- },
1133
- default: "/"
1134
- },
1135
- /**
1136
- * A key-value store of query parameters to be added to every selenium request
1137
- */
1138
- queryParams: {
1139
- type: "object"
1140
- },
1141
- /**
1142
- * cloud user if applicable
1143
- */
1144
- user: {
1145
- type: "string"
1146
- },
1147
- /**
1148
- * access key to user
1149
- */
1150
- key: {
1151
- type: "string"
1152
- },
1153
- /**
1154
- * capability of WebDriver session
1155
- */
1156
- capabilities: {
1157
- type: "object",
1158
- required: true
1159
- },
1160
- /**
1161
- * Level of logging verbosity
1162
- */
1163
- logLevel: {
1164
- type: "string",
1165
- default: "info",
1166
- match: /(trace|debug|info|warn|error|silent)/
1167
- },
1168
- /**
1169
- * directory for log files
1170
- */
1171
- outputDir: {
1172
- type: "string"
1173
- },
1174
- /**
1175
- * Timeout for any WebDriver request to a driver or grid
1176
- */
1177
- connectionRetryTimeout: {
1178
- type: "number",
1179
- default: 12e4
1180
- },
1181
- /**
1182
- * Count of request retries to the Selenium server
1183
- */
1184
- connectionRetryCount: {
1185
- type: "number",
1186
- default: 3
1187
- },
1188
- /**
1189
- * Override default agent
1190
- */
1191
- logLevels: {
1192
- type: "object"
1193
- },
1194
- /**
1195
- * Pass custom headers
1196
- */
1197
- headers: {
1198
- type: "object"
1199
- },
1200
- /**
1201
- * Function transforming the request options before the request is made
1202
- */
1203
- transformRequest: {
1204
- type: "function",
1205
- default: (requestOptions) => requestOptions
1206
- },
1207
- /**
1208
- * Function transforming the response object after it is received
1209
- */
1210
- transformResponse: {
1211
- type: "function",
1212
- default: (response) => response
1213
- },
1214
- /**
1215
- * Appium direct connect options server (https://appiumpro.com/editions/86-connecting-directly-to-appium-hosts-in-distributed-environments)
1216
- * Whether to allow direct connect caps to adjust endpoint details (Appium only)
1217
- */
1218
- enableDirectConnect: {
1219
- type: "boolean",
1220
- default: true
1221
- },
1222
- /**
1223
- * Whether it requires SSL certificates to be valid in HTTP/s requests
1224
- * for an environment which cannot get process environment well.
1225
- */
1226
- strictSSL: {
1227
- type: "boolean",
1228
- default: true
1229
- },
1230
- /**
1231
- * The path to the root of the cache directory. This directory is used to store all drivers that are downloaded
1232
- * when attempting to start a session.
1233
- */
1234
- cacheDir: {
1235
- type: "string",
1236
- default: process.env.WEBDRIVER_CACHE_DIR || os.tmpdir()
1503
+ };
1504
+ var WebDriverResponseError = class _WebDriverResponseError extends WebDriverError {
1505
+ constructor(response, url, opts) {
1506
+ const errorObj = !response.body ? new Error("Response has empty body") : typeof response.body === "string" && response.body.length ? new Error(response.body) : typeof response.body !== "object" ? new Error("Unknown error") : response.body.value || response.body;
1507
+ let errorMessage = errorObj.message || errorObj.error || errorObj.class || "unknown error";
1508
+ if (typeof errorMessage === "string" && errorMessage.includes("invalid locator")) {
1509
+ const requestOptions = opts.body;
1510
+ errorMessage = 'The selector "'.concat(requestOptions.value, '" used with strategy "').concat(requestOptions.using, '" is invalid!');
1511
+ }
1512
+ super(errorMessage);
1513
+ __publicField(this, "url");
1514
+ __publicField(this, "opts");
1515
+ if (errorObj.error) {
1516
+ this.name = errorObj.error;
1517
+ } else if (errorMessage && errorMessage.includes("stale element reference")) {
1518
+ this.name = "stale element reference";
1519
+ } else {
1520
+ this.name = errorObj.name || "WebDriver Error";
1521
+ }
1522
+ Error.captureStackTrace(this, _WebDriverResponseError);
1523
+ this.url = url;
1524
+ this.opts = opts;
1525
+ this.message = this.computeErrorMessage();
1237
1526
  }
1238
1527
  };
1239
- var ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
1240
- var SHADOW_ELEMENT_KEY = "shadow-6066-11e4-a52e-4f735466cecf";
1241
1528
 
1242
1529
  // package.json
1243
1530
  var package_default = {
1244
1531
  name: "webdriver",
1245
- version: "9.3.0",
1532
+ version: "9.3.1",
1246
1533
  description: "A Node.js bindings implementation for the W3C WebDriver and Mobile JSONWire Protocol",
1247
1534
  author: "Christian Bromann <mail@bromann.dev>",
1248
1535
  homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver",
1249
1536
  license: "MIT",
1250
- main: "./build/index.cjs",
1251
1537
  type: "module",
1538
+ main: "./build/index.cjs",
1252
1539
  module: "./build/index.js",
1253
1540
  exports: {
1254
1541
  ".": {
1255
1542
  types: "./build/index.d.ts",
1256
- import: "./build/index.js",
1543
+ browserSource: "./src/browser.js",
1544
+ browser: "./build/index.js",
1545
+ importSource: "./src/node.ts",
1546
+ import: "./build/node.js",
1257
1547
  requireSource: "./src/index.cts",
1258
1548
  require: "./build/index.cjs"
1259
1549
  }
@@ -1283,34 +1573,31 @@ var package_default = {
1283
1573
  "@wdio/types": "workspace:*",
1284
1574
  "@wdio/utils": "workspace:*",
1285
1575
  "deepmerge-ts": "^7.0.3",
1576
+ undici: "^6.20.1",
1286
1577
  ws: "^8.8.0"
1287
1578
  }
1288
1579
  };
1289
1580
 
1290
- // src/request/index.ts
1581
+ // src/request/request.ts
1291
1582
  var ERRORS_TO_EXCLUDE_FROM_RETRY = [
1292
1583
  "detached shadow root",
1293
1584
  "move target out of bounds"
1294
1585
  ];
1295
- var COMMANDS_WITHOUT_RETRY = [
1296
- findCommandPathByName("performActions")
1297
- ];
1298
1586
  var DEFAULT_HEADERS = {
1299
1587
  "Content-Type": "application/json; charset=utf-8",
1300
1588
  "Connection": "keep-alive",
1301
1589
  "Accept": "application/json",
1302
1590
  "User-Agent": "webdriver/" + package_default.version
1303
1591
  };
1304
- var log3 = logger3("webdriver");
1592
+ var log5 = logger5("webdriver");
1305
1593
  var WebDriverRequest = class extends EventEmitter {
1306
- #requestTimeout;
1307
- body;
1308
- method;
1309
- endpoint;
1310
- isHubCommand;
1311
- requiresSessionId;
1312
1594
  constructor(method, endpoint, body, isHubCommand = false) {
1313
1595
  super();
1596
+ __publicField(this, "body");
1597
+ __publicField(this, "method");
1598
+ __publicField(this, "endpoint");
1599
+ __publicField(this, "isHubCommand");
1600
+ __publicField(this, "requiresSessionId");
1314
1601
  this.body = body;
1315
1602
  this.method = method;
1316
1603
  this.endpoint = endpoint;
@@ -1318,25 +1605,15 @@ var WebDriverRequest = class extends EventEmitter {
1318
1605
  this.requiresSessionId = Boolean(this.endpoint.match(/:sessionId/));
1319
1606
  }
1320
1607
  async makeRequest(options, sessionId) {
1321
- const { url, requestOptions } = await this._createOptions(options, sessionId);
1322
- let fullRequestOptions = Object.assign(
1323
- { method: this.method },
1324
- requestOptions
1325
- );
1326
- if (typeof options.transformRequest === "function") {
1327
- fullRequestOptions = options.transformRequest(fullRequestOptions);
1328
- }
1329
- this.emit("request", fullRequestOptions);
1330
- return this._request(url, fullRequestOptions, options.transformResponse, options.connectionRetryCount, 0);
1331
- }
1332
- async _createOptions(options, sessionId, isBrowser = false) {
1333
- const controller = new AbortController();
1334
- this.#requestTimeout = setTimeout(
1335
- () => controller.abort(),
1336
- options.connectionRetryTimeout || DEFAULTS.connectionRetryTimeout.default
1337
- );
1608
+ const { url, requestOptions } = await this.createOptions(options, sessionId);
1609
+ this.emit("request", requestOptions);
1610
+ return this._request(url, requestOptions, options.transformResponse, options.connectionRetryCount, 0);
1611
+ }
1612
+ async createOptions(options, sessionId, isBrowser = false) {
1613
+ const timeout = options.connectionRetryTimeout || DEFAULTS.connectionRetryTimeout.default;
1338
1614
  const requestOptions = {
1339
- signal: controller.signal
1615
+ method: this.method,
1616
+ signal: AbortSignal.timeout(timeout)
1340
1617
  };
1341
1618
  const requestHeaders = new Headers({
1342
1619
  ...DEFAULT_HEADERS,
@@ -1346,7 +1623,7 @@ var WebDriverRequest = class extends EventEmitter {
1346
1623
  if (this.body && (Object.keys(this.body).length || this.method === "POST")) {
1347
1624
  const contentLength = Buffer.byteLength(JSON.stringify(this.body), "utf8");
1348
1625
  requestOptions.body = this.body;
1349
- requestHeaders.set("Content-Length", `${contentLength}`);
1626
+ requestHeaders.set("Content-Length", "".concat(contentLength));
1350
1627
  }
1351
1628
  let endpoint = this.endpoint;
1352
1629
  if (this.requiresSessionId) {
@@ -1355,7 +1632,7 @@ var WebDriverRequest = class extends EventEmitter {
1355
1632
  }
1356
1633
  endpoint = endpoint.replace(":sessionId", sessionId);
1357
1634
  }
1358
- const url = new URL(`${options.protocol}://${options.hostname}:${options.port}${this.isHubCommand ? this.endpoint : path.join(options.path || "", endpoint)}`);
1635
+ const url = new URL("".concat(options.protocol, "://").concat(options.hostname, ":").concat(options.port).concat(this.isHubCommand ? this.endpoint : path.join(options.path || "", endpoint)));
1359
1636
  if (searchParams) {
1360
1637
  url.search = new URLSearchParams(searchParams).toString();
1361
1638
  }
@@ -1363,29 +1640,48 @@ var WebDriverRequest = class extends EventEmitter {
1363
1640
  requestHeaders.set("Authorization", "Basic " + btoa(options.user + ":" + options.key));
1364
1641
  }
1365
1642
  requestOptions.headers = requestHeaders;
1366
- return { url, requestOptions };
1367
- }
1368
- async _libRequest(url, options) {
1369
- throw new Error("This function must be implemented");
1643
+ return {
1644
+ url,
1645
+ requestOptions: typeof options.transformRequest === "function" ? options.transformRequest(requestOptions) : requestOptions
1646
+ };
1370
1647
  }
1371
- _libPerformanceNow() {
1372
- throw new Error("This function must be implemented");
1648
+ async _libRequest(url, opts) {
1649
+ var _a;
1650
+ try {
1651
+ const response = await this.fetch(url, {
1652
+ method: opts.method,
1653
+ body: JSON.stringify(opts.body),
1654
+ headers: opts.headers,
1655
+ signal: opts.signal
1656
+ });
1657
+ const resp = response.clone();
1658
+ return {
1659
+ statusCode: resp.status,
1660
+ body: (_a = await resp.json()) != null ? _a : {}
1661
+ };
1662
+ } catch (err) {
1663
+ if (!(err instanceof Error)) {
1664
+ throw new WebDriverRequestError(
1665
+ new Error("Failed to fetch ".concat(url.href, ": ").concat(err.message || err || "Unknown error")),
1666
+ url,
1667
+ opts
1668
+ );
1669
+ }
1670
+ throw new WebDriverRequestError(err, url, opts);
1671
+ }
1373
1672
  }
1374
1673
  async _request(url, fullRequestOptions, transformResponse, totalRetryCount = 0, retryCount = 0) {
1375
- log3.info(`[${fullRequestOptions.method}] ${url.href}`);
1674
+ log5.info("[".concat(fullRequestOptions.method, "] ").concat(url.href));
1376
1675
  if (fullRequestOptions.body && Object.keys(fullRequestOptions.body).length) {
1377
- log3.info("DATA", transformCommandLogResult2(fullRequestOptions.body));
1676
+ log5.info("DATA", transformCommandLogResult2(fullRequestOptions.body));
1378
1677
  }
1379
1678
  const { ...requestLibOptions } = fullRequestOptions;
1380
- const startTime = this._libPerformanceNow();
1679
+ const startTime = performance.now();
1381
1680
  let response = await this._libRequest(url, requestLibOptions).catch((err) => err);
1382
- const durationMillisecond = this._libPerformanceNow() - startTime;
1383
- if (this.#requestTimeout) {
1384
- clearTimeout(this.#requestTimeout);
1385
- }
1681
+ const durationMillisecond = performance.now() - startTime;
1386
1682
  const retry = async (error2) => {
1387
1683
  if (retryCount >= totalRetryCount || error2.message.includes("invalid session id")) {
1388
- log3.error(error2.message);
1684
+ log5.error(error2.message);
1389
1685
  this.emit("response", { error: error2 });
1390
1686
  this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error: error2, retryCount });
1391
1687
  throw error2;
@@ -1396,8 +1692,8 @@ var WebDriverRequest = class extends EventEmitter {
1396
1692
  ++retryCount;
1397
1693
  this.emit("retry", { error: error2, retryCount });
1398
1694
  this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error: error2, retryCount });
1399
- log3.warn(error2.message);
1400
- log3.info(`Retrying ${retryCount}/${totalRetryCount}`);
1695
+ log5.warn(error2.message);
1696
+ log5.info("Retrying ".concat(retryCount, "/").concat(totalRetryCount));
1401
1697
  return this._request(url, fullRequestOptions, transformResponse, totalRetryCount, retryCount);
1402
1698
  };
1403
1699
  if (response instanceof Error) {
@@ -1425,7 +1721,7 @@ var WebDriverRequest = class extends EventEmitter {
1425
1721
  return { value: response.body || null };
1426
1722
  }
1427
1723
  if (error.name === "stale element reference") {
1428
- log3.warn("Request encountered a stale element - terminating request");
1724
+ log5.warn("Request encountered a stale element - terminating request");
1429
1725
  this.emit("response", { error });
1430
1726
  this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error, retryCount });
1431
1727
  throw error;
@@ -1436,310 +1732,29 @@ var WebDriverRequest = class extends EventEmitter {
1436
1732
  return retry(error);
1437
1733
  }
1438
1734
  };
1439
- function findCommandPathByName(commandName) {
1440
- const command = Object.entries(WebDriverProtocol2).find(
1441
- ([, command2]) => Object.values(command2).find(
1442
- (cmd) => cmd.command === commandName
1443
- )
1444
- );
1445
- if (!command) {
1446
- throw new Error(`Couldn't find command "${commandName}"`);
1447
- }
1448
- return command[0];
1449
- }
1450
1735
 
1451
- // src/request/request.ts
1452
- if ("process" in globalThis && globalThis.process.versions?.node) {
1453
- dns.setDefaultResultOrder("ipv4first");
1454
- }
1736
+ // src/request/web.ts
1455
1737
  var FetchRequest = class extends WebDriverRequest {
1456
- constructor(method, endpoint, body, isHubCommand = false) {
1457
- super(method, endpoint, body, isHubCommand);
1458
- }
1459
- async _libRequest(url, opts) {
1460
- try {
1461
- const response = await fetch(url, {
1462
- method: opts.method,
1463
- body: JSON.stringify(opts.body),
1464
- headers: opts.headers,
1465
- signal: opts.signal
1466
- });
1467
- const resp = response.clone();
1468
- return {
1469
- statusCode: resp.status,
1470
- body: await resp.json() ?? {}
1471
- };
1472
- } catch (err) {
1473
- if (!(err instanceof Error)) {
1474
- throw new WebDriverRequestError(
1475
- new Error(`Failed to fetch ${url.href}: ${err.message || err || "Unknown error"}`),
1476
- url,
1477
- opts
1478
- );
1479
- }
1480
- throw new WebDriverRequestError(err, url, opts);
1481
- }
1482
- }
1483
- _libPerformanceNow() {
1484
- return performance.now();
1738
+ fetch(url, opts) {
1739
+ return fetch(url, opts);
1485
1740
  }
1486
1741
  };
1487
1742
 
1488
- // src/command.ts
1489
- var log4 = logger4("webdriver");
1490
- var BIDI_COMMANDS = Object.values(WebDriverBidiProtocol2).map((def) => def.socket.command);
1491
- function command_default(method, endpointUri, commandInfo, doubleEncodeVariables = false) {
1492
- const { command, deprecated, ref, parameters, variables = [], isHubCommand = false } = commandInfo;
1493
- return async function protocolCommand(...args) {
1494
- const isBidiCommand = BIDI_COMMANDS.includes(command);
1495
- let endpoint = endpointUri;
1496
- const commandParams = [...variables.map((v) => Object.assign(v, {
1497
- /**
1498
- * url variables are:
1499
- */
1500
- required: true,
1501
- // always required as they are part of the endpoint
1502
- type: "string"
1503
- // have to be always type of string
1504
- })), ...parameters];
1505
- const commandUsage = `${command}(${commandParams.map((p) => p.name).join(", ")})`;
1506
- const moreInfo = `
1507
-
1508
- For more info see ${ref}
1509
- `;
1510
- const body = {};
1511
- if (typeof deprecated === "string" && !process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS) {
1512
- const warning = deprecated.replace("This command", `The "${command}" command`);
1513
- log4.warn(warning);
1514
- console.warn(`\u26A0\uFE0F [WEBDRIVERIO DEPRECATION NOTICE] ${warning}`);
1515
- }
1516
- if (isBidiCommand) {
1517
- throw new Error(
1518
- `Failed to execute WebDriver Bidi command "${command}" as no Bidi session was established. Make sure you enable it by setting "webSocketUrl: true" in your capabilities and verify that your environment and browser supports it.`
1519
- );
1520
- }
1521
- const minAllowedParams = commandParams.filter((param) => param.required).length;
1522
- if (args.length < minAllowedParams || args.length > commandParams.length) {
1523
- const parameterDescription = commandParams.length ? `
1524
-
1525
- Property Description:
1526
- ${commandParams.map((p) => ` "${p.name}" (${p.type}): ${p.description}`).join("\n")}` : "";
1527
- throw new Error(
1528
- `Wrong parameters applied for ${command}
1529
- Usage: ${commandUsage}` + parameterDescription + moreInfo
1530
- );
1531
- }
1532
- for (const [it, arg] of Object.entries(args)) {
1533
- if (isBidiCommand) {
1534
- break;
1535
- }
1536
- const i = parseInt(it, 10);
1537
- const commandParam = commandParams[i];
1538
- if (!isValidParameter(arg, commandParam.type)) {
1539
- if (typeof arg === "undefined" && !commandParam.required) {
1540
- continue;
1541
- }
1542
- const actual = commandParam.type.endsWith("[]") ? `(${(Array.isArray(arg) ? arg : [arg]).map((a) => getArgumentType(a))})[]` : getArgumentType(arg);
1543
- throw new Error(
1544
- `Malformed type for "${commandParam.name}" parameter of command ${command}
1545
- Expected: ${commandParam.type}
1546
- Actual: ${actual}` + moreInfo
1547
- );
1548
- }
1549
- if (i < variables.length) {
1550
- const encodedArg = doubleEncodeVariables ? encodeURIComponent(encodeURIComponent(arg)) : encodeURIComponent(arg);
1551
- endpoint = endpoint.replace(`:${commandParams[i].name}`, encodedArg);
1552
- continue;
1553
- }
1554
- body[commandParams[i].name] = arg;
1555
- }
1556
- const request = new FetchRequest(method, endpoint, body, isHubCommand);
1557
- request.on("performance", (...args2) => this.emit("request.performance", ...args2));
1558
- this.emit("command", { command, method, endpoint, body });
1559
- log4.info("COMMAND", commandCallStructure(command, args));
1560
- return request.makeRequest(this.options, this.sessionId).then((result) => {
1561
- if (typeof result.value !== "undefined") {
1562
- let resultLog = result.value;
1563
- if (/screenshot|recording/i.test(command) && typeof result.value === "string" && result.value.length > 64) {
1564
- resultLog = `${result.value.slice(0, 61)}...`;
1565
- } else if (command === "executeScript" && body.script && body.script.includes("(() => window.__wdioEvents__)")) {
1566
- resultLog = `[${result.value.length} framework events captured]`;
1567
- }
1568
- log4.info("RESULT", resultLog);
1569
- }
1570
- this.emit("result", { command, method, endpoint, body, result });
1571
- if (command === "deleteSession") {
1572
- const browser = this;
1573
- browser._bidiHandler?.close();
1574
- const shutdownDriver = body.deleteSessionOpts?.shutdownDriver !== false;
1575
- if (shutdownDriver && "wdio:driverPID" in this.capabilities && this.capabilities["wdio:driverPID"]) {
1576
- log4.info(`Kill driver process with PID ${this.capabilities["wdio:driverPID"]}`);
1577
- try {
1578
- const killedSuccessfully = process.kill(this.capabilities["wdio:driverPID"], "SIGKILL");
1579
- if (!killedSuccessfully) {
1580
- log4.warn("Failed to kill driver process, manually clean-up might be required");
1581
- }
1582
- } catch (err) {
1583
- log4.warn("Failed to kill driver process", err);
1584
- }
1585
- setTimeout(() => {
1586
- for (const handle of process._getActiveHandles()) {
1587
- if (handle.servername && handle.servername.includes("edgedl.me")) {
1588
- handle.destroy();
1589
- }
1590
- }
1591
- }, 10);
1592
- }
1593
- if (!process.env.WDIO_WORKER_ID) {
1594
- logger4.clearLogger();
1595
- }
1596
- }
1597
- return result.value;
1598
- });
1599
- };
1600
- }
1601
-
1602
- // src/bidi/localTypes.ts
1603
- var localTypes_exports = {};
1604
-
1605
- // src/bidi/remoteTypes.ts
1606
- var remoteTypes_exports = {};
1607
-
1608
- // src/index.ts
1609
- var log5 = logger5("webdriver");
1610
- var WebDriver = class _WebDriver {
1611
- static async newSession(options, modifier, userPrototype = {}, customCommandWrapper) {
1612
- const envLogLevel = process.env.WDIO_LOG_LEVEL;
1613
- options.logLevel = envLogLevel ?? options.logLevel;
1614
- const params = validateConfig(DEFAULTS, options);
1615
- if (params.logLevel && (!options.logLevels || !options.logLevels.webdriver)) {
1616
- logger5.setLevel("webdriver", params.logLevel);
1617
- }
1618
- log5.info("Initiate new session using the WebDriver protocol");
1619
- const driverProcess = await startWebDriver(params);
1620
- const requestedCapabilities = { ...params.capabilities };
1621
- const { sessionId, capabilities } = await startWebDriverSession(params);
1622
- const environment = sessionEnvironmentDetector({ capabilities, requestedCapabilities });
1623
- const environmentPrototype = getEnvironmentVars(environment);
1624
- const protocolCommands = getPrototype(environment);
1625
- if (driverProcess?.pid) {
1626
- capabilities["wdio:driverPID"] = driverProcess.pid;
1627
- }
1628
- const bidiPrototype = {};
1629
- if (isBidi(requestedCapabilities, capabilities)) {
1630
- log5.info(`Register BiDi handler for session with id ${sessionId}`);
1631
- Object.assign(bidiPrototype, initiateBidi(capabilities.webSocketUrl, options.strictSSL));
1632
- }
1633
- const monad = webdriverMonad(
1634
- { ...params, requestedCapabilities },
1635
- modifier,
1636
- {
1637
- ...protocolCommands,
1638
- ...environmentPrototype,
1639
- ...userPrototype,
1640
- ...bidiPrototype
1641
- }
1642
- );
1643
- const client = monad(sessionId, customCommandWrapper);
1644
- if (isBidi(requestedCapabilities, capabilities)) {
1645
- await client._bidiHandler.waitForConnected();
1646
- client._bidiHandler.socket.on("message", parseBidiMessage.bind(client));
1647
- }
1648
- if (params.enableDirectConnect) {
1649
- setupDirectConnect(client);
1650
- }
1651
- return client;
1652
- }
1653
- /**
1654
- * allows user to attach to existing sessions
1655
- */
1656
- static attachToSession(options, modifier, userPrototype = {}, commandWrapper) {
1657
- if (!options || typeof options.sessionId !== "string") {
1658
- throw new Error("sessionId is required to attach to existing session");
1659
- }
1660
- if (options.logLevel) {
1661
- logger5.setLevel("webdriver", options.logLevel);
1662
- }
1663
- options.capabilities = options.capabilities || {};
1664
- options.isW3C = options.isW3C === false ? false : true;
1665
- options.protocol = options.protocol || DEFAULTS.protocol.default;
1666
- options.hostname = options.hostname || DEFAULTS.hostname.default;
1667
- options.port = options.port || DEFAULTS.port.default;
1668
- options.path = options.path || DEFAULTS.path.default;
1669
- const environment = sessionEnvironmentDetector({ capabilities: options.capabilities, requestedCapabilities: options.capabilities });
1670
- options = Object.assign(environment, options);
1671
- const environmentPrototype = getEnvironmentVars(options);
1672
- const protocolCommands = getPrototype(options);
1673
- const bidiPrototype = {};
1674
- if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
1675
- const webSocketUrl = options.capabilities?.webSocketUrl;
1676
- log5.info(`Register BiDi handler for session with id ${options.sessionId}`);
1677
- Object.assign(bidiPrototype, initiateBidi(webSocketUrl, options.strictSSL));
1678
- }
1679
- const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype };
1680
- const monad = webdriverMonad(options, modifier, prototype);
1681
- const client = monad(options.sessionId, commandWrapper);
1682
- if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
1683
- client._bidiHandler?.socket.on("message", parseBidiMessage.bind(client));
1684
- }
1685
- return client;
1686
- }
1687
- /**
1688
- * Changes The instance session id and browser capabilities for the new session
1689
- * directly into the passed in browser object
1690
- *
1691
- * @param {object} instance the object we get from a new browser session.
1692
- * @returns {string} the new session id of the browser
1693
- */
1694
- static async reloadSession(instance, newCapabilities) {
1695
- const capabilities = newCapabilities ? newCapabilities : Object.assign({}, instance.requestedCapabilities);
1696
- let params = { ...instance.options, capabilities };
1697
- for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
1698
- if (prop in capabilities) {
1699
- params = { ...params, [prop]: capabilities[prop] };
1700
- delete capabilities[prop];
1701
- }
1702
- }
1703
- let driverProcess;
1704
- if (params.hostname === "localhost" && newCapabilities?.browserName) {
1705
- delete params.port;
1706
- delete params.hostname;
1707
- driverProcess = await startWebDriver(params);
1708
- }
1709
- const { sessionId, capabilities: newSessionCapabilities } = await startWebDriverSession(params);
1710
- if (driverProcess?.pid) {
1711
- newSessionCapabilities["wdio:driverPID"] = driverProcess.pid;
1712
- }
1713
- for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
1714
- if (prop in params) {
1715
- instance.options[prop] = params[prop];
1716
- }
1717
- }
1718
- for (const prop in instance.requestedCapabilities) {
1719
- delete instance.requestedCapabilities[prop];
1720
- }
1721
- const driverPid = instance.capabilities["wdio:driverPID"];
1722
- instance.sessionId = sessionId;
1723
- instance.capabilities = newSessionCapabilities;
1724
- instance.capabilities["wdio:driverPID"] = driverPid;
1725
- Object.assign(instance.requestedCapabilities, capabilities);
1726
- if (isBidi(instance.requestedCapabilities || {}, instance.capabilities || {})) {
1727
- const bidiReqOpts = instance.options.strictSSL ? {} : { rejectUnauthorized: false };
1728
- await instance._bidiHandler?.reconnect(newSessionCapabilities.webSocketUrl, bidiReqOpts);
1729
- }
1730
- return sessionId;
1731
- }
1732
- static get WebDriver() {
1733
- return _WebDriver;
1734
- }
1743
+ // src/browser.ts
1744
+ var browser_default = WebDriver;
1745
+ environment.value = {
1746
+ Request: FetchRequest,
1747
+ Socket: BrowserSocket,
1748
+ variables: {}
1735
1749
  };
1736
1750
  export {
1737
1751
  BidiHandler,
1738
1752
  DEFAULTS,
1739
1753
  ELEMENT_KEY,
1740
1754
  SHADOW_ELEMENT_KEY,
1755
+ WebDriver,
1741
1756
  command_default as command,
1742
- WebDriver as default,
1757
+ browser_default as default,
1743
1758
  getEnvironmentVars,
1744
1759
  getPrototype,
1745
1760
  initiateBidi,