Repository: appium/appium-inspector (2025-07-12T12:58:17Z)

Issue:
browser build doesn't work in Safari
Some bundled dependency has an invalid regex according to Safari.
Web2Driver Integration Overview
The Inspector's core functionality of creating an inspectable client session currently relies on the `web2driver` package, which in turn proxies this request to the `webdriver` package (_not_ `webdriverio`).
However, `web2driver` `v3.0.4` still uses `webdriver` `v7`, which is outdated and missing a lot of newer functionality. Since `web2driver` is not maintained by Appium, and is relatively small (~250 lines), there is a benefit in integrating its contents directly into the Inspector code, allowing for faster and easier sync with new WDIO features.

This issue will be used to track findings related to this integration.

### First Attempts

The initial starting point was to copy the `web2driver` code, add licensing disclaimers, and replace the imports accordingly. With minimal code changes, this should have basically worked out of the box.
Unfortunately this did not work - it appears `webdriver` `v7` (and even `v8`) have cyclical dependencies ([here](https://github.com/webdriverio/webdriverio/blob/v7/packages/webdriver/src/request/index.ts#L4) and [here](https://github.com/webdriverio/webdriverio/blob/v7/packages/webdriver/src/request/factory.ts#L1)), which prevented session creation. Thankfully, this code was changed in `webdriver` `v9`, removing the cyclical dependency.
Since `v7/v8` would be unlikely to be patched, the only way forward was to jump directly to `webdriver` `v9`, which would likely come with additional code adjustments.

### First Attempts with v9

Building the app after bumping `webdriver` to `v9` (specifically, `9.14.0`) was now throwing errors at build-time. This was related to `@wdio/protocols`: `v9` dropped support for JSONWP, which was imported by the `web2driver` code. After removing this import, the build process succeeded - and creating a new session worked right away! 🎉 
However, when trying out the web version, session creation was still throwing errors. These were tracked down to imports of Node.js built-ins - namely, `node:buffer` and `node:events`. Adding the `vite-plugin-node-polyfills` dependency for handling these fixed the issue, and the session could now be created via web as well! 🎉 

### Exploring v9

At this point, session creation was working with basically no immediately observable issues: screenshot and source loaded fine, elements could be selected, searched for and clicked, and basic driver actions could also be executed. However, a few issues were nonetheless identified:

* Since the Inspector [uses several JSONWP methods](https://github.com/appium/appium-inspector/issues/2016), removing support for the protocol resulted in errors upon their invocation:
  * `getSession`, `getGeoLocation`, `setGeoLocation` - no alternatives ❌ 
  * `getOrientation`, `getOrientation` - moved to Appium protocol, no issues ✅ 
* While `webdriver` `v9` still supports the MJSONWP protocol, its methods `getContext`/`switchContext`/`getContexts` were moved to the Appium protocol and renamed to `getAppiumContext`/`switchAppiumContext`/`getAppiumContexts`, respectively. After changing the names of these methods in the Inspector code, they continued to work as expected. ✅ 
* Session creation via web works properly only in dev mode. Production mode (preview) is unable to be built, since `vite-plugin-node-polyfills` [can't yet handle `node:fs/promises`](https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/118)

### Proposed WDIO Changes

* Add `getSession`, `getGeoLocation` and `setGeoLocation` commands to the Appium protocol
  * Added in https://github.com/webdriverio/webdriverio/pull/14507
* Investigate the feasibility of removing the dependencies on `node:buffer` and `node:events`. There have already been previous changes with the goal of removing Node.js dependencies (see [PR #14049](https://github.com/webdriverio/webdriverio/pull/14049)), so this would be the logical next step.
  * Fixed in https://github.com/webdriverio/webdriverio/pull/14592

### After WDIO Changes

With the removal of Node.js dependencies, `vite-plugin-node-polyfills` was no longer necessary for a successful session creation via web in dev mode. Production mode still failed though, but a workaround was found in setting `globalThis.window` to `true` only during the `vite build` process. This allowed both the dev and production builds to work as expected! 🎉 
The browser build was also tested in Safari, and confirmed to finally work!


Discussion:
Results of my investigation:

* the dependency which appears to be the problem is [normalize-url](https://github.com/sindresorhus/normalize-url).
* normalize-url appears to have received a [fix](https://github.com/sindresorhus/normalize-url/pull/148) which is published in [7.0.1](https://github.com/sindresorhus/normalize-url/releases/tag/v7.0.1)
* normalize-url appears in our dep tree in many places, but the most likely dep path for the error to have occurred is: `web2driver` > `webdriver` > `got` > `cacheable-request` > `normalize-url`.
* I tried to submit a PR to cacheable-request with an updated version of normalize-url, but it appears that v7.0.0 of normalize-url was a big breaking change, making the module ESM only. to support it from cacheable-request looks to be a sizable change. I created [an issue](https://github.com/lukechilds/cacheable-request/issues/106) at the cacheable-request repo to seek further guidance.
* Even if the cacheable-request step is fixed, it looks like we'd need to trigger updates in `got`, `webdriver`, and `web2driver`. Hoping that would be easier, though I'm not sure how well ESM plays with the build systems for web2driver and the inspector.

As of now, it looks as if there's no path forward to fixing this particular issue.
* `normalize-url` 7.0.3 is included in `cacheable-request` 8.0.1
* `cacheable-request` v8 (actually v10) is included in `got` 12.5.0
* `got` 12.5.0 (actually 12.6.1) is included in `webdriver` 8.10.6

Seems like all that's left is to bump `web2driver` to this `webdriver` version or newer. It needs to be `webdriver` v8, because `webdriver` v7 is currently still on `got` 11.
OK, I'll make a task to update web2driver to latest webdriver

It makers sense to use mobile wrappers instead of getGeoLocation and setGeoLocation endpoints. They are driver-specific anyway
True, they can definitely be removed from the Inspector. But they're still supported in Appium 3, so I figured it made sense to add them to WDIO as well.
It would probably be a good idea to also deprecate them for good from the base driver



Patch:
diff --git a/app/common/renderer/actions/Inspector.js b/app/common/renderer/actions/Inspector.js
index 1ceb31ce01..bd16230370 100644
--- a/app/common/renderer/actions/Inspector.js
+++ b/app/common/renderer/actions/Inspector.js
@@ -712,7 +712,7 @@ export function selectAppMode(mode) {
       await action(dispatch, getState);
     }
     if (appMode !== mode && mode === APP_MODE.NATIVE) {
-      const action = applyClientMethod({methodName: 'switchContext', args: [NATIVE_APP]});
+      const action = applyClientMethod({methodName: 'switchAppiumContext', args: [NATIVE_APP]});
       await action(dispatch, getState);
     }
   };
diff --git a/app/common/renderer/actions/Session.js b/app/common/renderer/actions/Session.js
index 2982fa8464..7fcb8843f7 100644
--- a/app/common/renderer/actions/Session.js
+++ b/app/common/renderer/actions/Session.js
@@ -1,5 +1,4 @@
 import _ from 'lodash';
-import {Web2Driver} from 'web2driver';
 
 import {
   SAVED_SESSIONS,
@@ -11,6 +10,7 @@ import {
 import {SERVER_TYPES, SESSION_BUILDER_TABS} from '../constants/session-builder';
 import {APP_MODE} from '../constants/session-inspector';
 import i18n from '../i18next';
+import Web2Driver from '../lib/appium/driver.js';
 import {VENDOR_MAP} from '../lib/vendor/map.js';
 import {getSetting, ipcRenderer, setSetting} from '../polyfills';
 import {fetchSessionInformation, formatSeleniumGridSessions} from '../utils/attaching-to-session';
@@ -326,7 +326,7 @@ export function newSession(originalCaps, attachSessId = null) {
         const platformName = attachedSessionCaps.platformName || attachedSessionCaps.platform;
         serverOpts.isIOS = Boolean(platformName.match(/iOS/i));
         serverOpts.isAndroid = Boolean(platformName.match(/Android/i));
-        driver = await Web2Driver.attachToSession(attachSessId, serverOpts, attachedSessionCaps);
+        driver = Web2Driver.attachToSession(attachSessId, serverOpts, attachedSessionCaps);
         driver._isAttachedSession = true;
       } else {
         driver = await Web2Driver.remote(serverOpts, sessionCaps);
diff --git a/app/common/renderer/components/Inspector/HeaderButtons.jsx b/app/common/renderer/components/Inspector/HeaderButtons.jsx
index e18d82e80e..0e0ed595a4 100644
--- a/app/common/renderer/components/Inspector/HeaderButtons.jsx
+++ b/app/common/renderer/components/Inspector/HeaderButtons.jsx
@@ -146,7 +146,7 @@ const HeaderButtons = (props) => {
             popupMatchSelectWidth={false}
             onChange={(value) => {
               setContext(value);
-              applyClientMethod({methodName: 'switchContext', args: [value]});
+              applyClientMethod({methodName: 'switchAppiumContext', args: [value]});
             }}
           >
             {contexts.map(({id, title}) => (
diff --git a/app/common/renderer/constants/commands.js b/app/common/renderer/constants/commands.js
index c5d365971f..b2f439d4a6 100644
--- a/app/common/renderer/constants/commands.js
+++ b/app/common/renderer/constants/commands.js
@@ -39,9 +39,9 @@ export const COMMAND_DEFINITIONS = {
     },
   },
   Context: {
-    getContext: {},
-    getContexts: {},
-    switchContext: {
+    getAppiumContext: {},
+    getAppiumContexts: {},
+    switchAppiumContext: {
       args: [['name', STRING]],
       refresh: true,
     },
diff --git a/app/common/renderer/lib/appium/driver.js b/app/common/renderer/lib/appium/driver.js
new file mode 100644
index 0000000000..b3e6f9a2da
--- /dev/null
+++ b/app/common/renderer/lib/appium/driver.js
@@ -0,0 +1,78 @@
+/**
+ *    Copyright 2024 HeadSpin, Inc.
+ *    Modifications copyright OpenJS Foundation and other contributors,
+ *    https://openjsf.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import webdriver from 'webdriver';
+
+import Session from './session';
+
+export const DEFAULTS = {
+  protocol: 'http',
+  hostname: '0.0.0.0',
+  port: 4444,
+  path: '/wd/hub',
+  logLevel: typeof window === 'undefined' ? 'silent' : 'info',
+};
+
+export default class Web2Driver {
+  static async remote(
+    {
+      protocol = DEFAULTS.protocol,
+      hostname = DEFAULTS.hostname,
+      port = DEFAULTS.port,
+      path = DEFAULTS.path,
+      logLevel = DEFAULTS.logLevel,
+      ...otherParams
+    },
+    capabilities = {},
+  ) {
+    const params = {protocol, hostname, port, path, capabilities, logLevel, ...otherParams};
+    const sessionClient = await webdriver.newSession(params);
+    return new Session(sessionClient);
+  }
+
+  static attachToSession(
+    sessionId,
+    {
+      protocol = DEFAULTS.protocol,
+      hostname = DEFAULTS.hostname,
+      port = DEFAULTS.port,
+      path = DEFAULTS.path,
+      logLevel = DEFAULTS.logLevel,
+      ...otherParams
+    },
+    capabilities = {},
+    isW3C = true,
+  ) {
+    if (!sessionId) {
+      throw new Error("Can't attach to a session without a session id");
+    }
+    const params = {
+      sessionId,
+      isW3C,
+      protocol,
+      hostname,
+      port,
+      path,
+      capabilities,
+      logLevel,
+      ...otherParams,
+    };
+    const sessionClient = webdriver.attachToSession(params);
+    return new Session(sessionClient);
+  }
+}
diff --git a/app/common/renderer/lib/appium/element.js b/app/common/renderer/lib/appium/element.js
new file mode 100644
index 0000000000..89434e5128
--- /dev/null
+++ b/app/common/renderer/lib/appium/element.js
@@ -0,0 +1,86 @@
+/**
+ *    Copyright 2024 HeadSpin, Inc.
+ *    Modifications copyright OpenJS Foundation and other contributors,
+ *    https://openjsf.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import _ from 'lodash';
+
+export const W3C_ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf';
+export const JWP_ELEMENT_KEY = 'ELEMENT';
+
+export default class UIElement {
+  constructor(elementKey, findRes, parent) {
+    this.elementKey = elementKey;
+    this.elementId = this[elementKey] = findRes[elementKey];
+    this.__is_w2d_element = true;
+    this.parent = parent;
+    this.session = parent.session || parent;
+  }
+
+  get executeObj() {
+    return {[this.elementKey]: this.elementId};
+  }
+
+  async findElement(using, value) {
+    const res = await this.session.cmd('findElementFromElement', this.elementId, using, value);
+    return getElementFromResponse(res, this);
+  }
+
+  async findElements(using, value) {
+    const ress = await this.session.cmd('findElementsFromElement', this.elementId, using, value);
+    return ress.map((res) => getElementFromResponse(res, this));
+  }
+}
+
+export function getElementFromResponse(res, parent) {
+  let elementKey;
+  if (res[W3C_ELEMENT_KEY]) {
+    elementKey = W3C_ELEMENT_KEY;
+  } else {
+    elementKey = JWP_ELEMENT_KEY;
+  }
+
+  if (!res[elementKey]) {
+    throw new Error(
+      `Bad findElement response; did not have element key. ` +
+        `Response was: ${JSON.stringify(res)}`,
+    );
+  }
+
+  return new UIElement(elementKey, res, parent);
+}
+
+export const ELEMENT_CMDS = {
+  isElementSelected: 'isSelected',
+  isElementDisplayed: 'isDisplayed',
+  getElementAttribute: 'getAttribute',
+  getElementCSSValue: 'getCSSValue',
+  getElementText: 'getText',
+  getElementTagName: 'getTagName',
+  getElementProperty: 'getProperty',
+  getElementRect: 'getRect',
+  getElementEnabled: 'getEnabled',
+  elementClick: 'click',
+  elementClear: 'clear',
+  elementSendKeys: 'sendKeys',
+  takeElementScreenshot: 'takeScreenshot',
+};
+
+for (const [protoCmd, newCmd] of _.toPairs(ELEMENT_CMDS)) {
+  UIElement.prototype[newCmd] = async function (...args) {
+    return await this.session.cmd(protoCmd, this.elementId, ...args);
+  };
+}
diff --git a/app/common/renderer/lib/appium/inspector-driver.js b/app/common/renderer/lib/appium/inspector-driver.js
index 685876bbf6..25a6c8f25f 100644
--- a/app/common/renderer/lib/appium/inspector-driver.js
+++ b/app/common/renderer/lib/appium/inspector-driver.js
@@ -272,14 +272,14 @@ export default class InspectorDriver {
 
     // First get the current context (or the error, if one appears)
     try {
-      currentContext = await this.driver.getContext();
+      currentContext = await this.driver.getAppiumContext();
     } catch (e) {
       currentContextError = e;
     }
 
     // The retrieval of all contexts and webview position adjustments require some native context use
     if (currentContext !== NATIVE_APP) {
-      await this.driver.switchContext(NATIVE_APP);
+      await this.driver.switchAppiumContext(NATIVE_APP);
     }
 
     const isAndroid = this.driver.client.isAndroid;
@@ -375,7 +375,7 @@ export default class InspectorDriver {
       }
 
       // Native context calculation part is done - switch back to webview context
-      await this.driver.switchContext(currentContext);
+      await this.driver.switchAppiumContext(currentContext);
 
       // Adjust all elements by the calculated offsets
       await this.driver.executeScript(
@@ -413,7 +413,7 @@ export default class InspectorDriver {
    */
   async hasContextsCommand() {
     try {
-      await this.driver.getContexts();
+      await this.driver.getAppiumContexts();
       return true;
     } catch {}
 
diff --git a/app/common/renderer/lib/appium/session.js b/app/common/renderer/lib/appium/session.js
new file mode 100644
index 0000000000..b7f4b71cc0
--- /dev/null
+++ b/app/common/renderer/lib/appium/session.js
@@ -0,0 +1,156 @@
+/**
+ *    Copyright 2024 HeadSpin, Inc.
+ *    Modifications copyright OpenJS Foundation and other contributors,
+ *    https://openjsf.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {AppiumProtocol, MJsonWProtocol, WebDriverProtocol} from '@wdio/protocols';
+import _ from 'lodash';
+
+import {ELEMENT_CMDS, getElementFromResponse} from './element';
+
+export default class Session {
+  constructor(wdSessionClient) {
+    this.client = wdSessionClient;
+  }
+
+  async cmd(commandName, ...args) {
+    const res = await this.client[commandName](...args);
+    if (res && res.error) {
+      throw new Error(res.message ? res.message : res.error);
+    }
+    return res;
+  }
+
+  get connectedUrl() {
+    const {protocol, hostname, port, path} = this.client.options;
+    return `${protocol}://${hostname}:${port}${path}`;
+  }
+
+  get sessionId() {
+    return this.client.sessionId;
+  }
+
+  get capabilities() {
+    return this.client.options.capabilities;
+  }
+
+  async findElement(using, value) {
+    const res = await this.cmd('findElement', using, value);
+    return getElementFromResponse(res, this);
+  }
+
+  async findElements(using, value) {
+    const ress = await this.cmd('findElements', using, value);
+    return ress.map((res) => getElementFromResponse(res, this));
+  }
+
+  async waitForElement(ms, using, value) {
+    let el = null;
+    const start = Date.now();
+    const end = start + ms;
+    while (el === null && Date.now() < end) {
+      try {
+        el = await this.findElement(using, value);
+      } catch {}
+    }
+
+    if (el) {
+      return el;
+    }
+
+    throw new Error(
+      `Could not find element using strategy ${using} and value '${value}' after ${ms}ms`,
+    );
+  }
+
+  async waitForElements(ms, using, value) {
+    let els = [];
+    const start = Date.now();
+    const end = start + ms;
+    while (els.length === 0 && Date.now() < end) {
+      els = await this.findElements(using, value);
+    }
+
+    if (els.length) {
+      return els;
+    }
+
+    throw new Error(
+      `Could not find any elements using strategy ${using} and value '${value}' after ${ms}ms`,
+    );
+  }
+
+  async executeBase(cmd, script, args) {
+    args = args.map((a) => {
+      if (a.__is_w2d_element) {
+        return a.executeObj;
+      }
+      return a;
+    });
+    return await this.cmd(cmd, script, args);
+  }
+
+  async executeScript(script, args) {
+    return await this.executeBase('executeScript', script, args);
+  }
+
+  async executeAsyncScript(script, args) {
+    return await this.executeBase('executeAsyncScript', script, args);
+  }
+}
+
+const AVOID_CMDS = [
+  'newSession',
+  'findElement',
+  'findElements',
+  'findElementFromElement',
+  'findElementsFromElement',
+  'executeScript',
+  'executeAsyncScript',
+];
+
+const ALIAS_CMDS = {
+  deleteSession: 'quit',
+};
+
+// here we walk through the protocol specification from the webdriver package
+// and simply put all the methods on Session (except for element methods and
+// edge cases)
+
+for (const proto of [WebDriverProtocol, MJsonWProtocol, AppiumProtocol]) {
+  for (const [, methods] of _.toPairs(proto)) {
+    for (const [, cmdData] of _.toPairs(methods)) {
+      // if we've explicitly asked not to include the command, skip it
+      if (AVOID_CMDS.includes(cmdData.command)) {
+        continue;
+      }
+
+      // likewise skip element commands; those are handled by element.js
+      if (_.keys(ELEMENT_CMDS).includes(cmdData.command)) {
+        continue;
+      }
+
+      // give the command a new name if we so desire
+      const cmdName = _.keys(ALIAS_CMDS).includes(cmdData.command)
+        ? ALIAS_CMDS[cmdData.command]
+        : cmdData.command;
+
+      Session.prototype[cmdName] = async function (...args) {
+        return await this.cmd(cmdData.command, ...args);
+      };
+    }
+  }
+}
diff --git a/app/common/renderer/lib/client-frameworks/dotnet-nunit.js b/app/common/renderer/lib/client-frameworks/dotnet-nunit.js
index 39446fcdc5..25c57a69de 100644
--- a/app/common/renderer/lib/client-frameworks/dotnet-nunit.js
+++ b/app/common/renderer/lib/client-frameworks/dotnet-nunit.js
@@ -224,15 +224,15 @@ let timeouts = new Dictionary<string, TimeSpan>()
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return `var context = _driver.Context;`;
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return `var contexts = _driver.Contexts;`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `_driver.Context = "${name}";`;
   }
 
diff --git a/app/common/renderer/lib/client-frameworks/java-common.js b/app/common/renderer/lib/client-frameworks/java-common.js
index 5759ec72e7..44909c82de 100644
--- a/app/common/renderer/lib/client-frameworks/java-common.js
+++ b/app/common/renderer/lib/client-frameworks/java-common.js
@@ -196,15 +196,15 @@ var timeouts = Map.ofEntries(
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return `var context = driver.getContext();`;
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return `var contexts = driver.getContextHandles();`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `driver.context("${name}");`;
   }
 
diff --git a/app/common/renderer/lib/client-frameworks/js-oxygen.js b/app/common/renderer/lib/client-frameworks/js-oxygen.js
index 16d474ba5d..bb24b131e9 100644
--- a/app/common/renderer/lib/client-frameworks/js-oxygen.js
+++ b/app/common/renderer/lib/client-frameworks/js-oxygen.js
@@ -126,15 +126,15 @@ ${code}`;
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return `let context = ${this.type}.getDriver().getContext();`;
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return `let contexts = ${this.type}.getDriver().getContexts();`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `${this.type}.setContext("${name}");`;
   }
 
diff --git a/app/common/renderer/lib/client-frameworks/js-wdio.js b/app/common/renderer/lib/client-frameworks/js-wdio.js
index 585151b5d2..100fd42d7b 100644
--- a/app/common/renderer/lib/client-frameworks/js-wdio.js
+++ b/app/common/renderer/lib/client-frameworks/js-wdio.js
@@ -5,7 +5,7 @@ export default class JsWdIoFramework extends CommonClientFramework {
   static highlightLang = 'js';
 
   wrapWithBoilerplate(code) {
-    return `// This sample code supports WebdriverIO client >=8
+    return `// This sample code supports WebdriverIO client >=9.7.0
 // (npm i --save webdriverio)
 // Then paste this into a .js file and run with Node:
 // node <file>.js
@@ -135,16 +135,16 @@ main().catch(console.log);`;
 
   // Context
 
-  codeFor_getContext() {
-    return `let context = await driver.getContext();`;
+  codeFor_getAppiumContext() {
+    return `let context = await driver.getAppiumContext();`;
   }
 
-  codeFor_getContexts() {
-    return `let contexts = await driver.getContexts();`;
+  codeFor_getAppiumContexts() {
+    return `let contexts = await driver.getAppiumContexts();`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
-    return `await driver.switchContext("${name}");`;
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
+    return `await driver.switchAppiumContext("${name}");`;
   }
 
   // Device Interaction
diff --git a/app/common/renderer/lib/client-frameworks/python.js b/app/common/renderer/lib/client-frameworks/python.js
index c99f7ae7aa..dcbaa22819 100644
--- a/app/common/renderer/lib/client-frameworks/python.js
+++ b/app/common/renderer/lib/client-frameworks/python.js
@@ -165,15 +165,15 @@ actions.perform()
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return `context = driver.current_context`;
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return `contexts = driver.contexts`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `driver.switch_to.context('${name}')`;
   }
 
diff --git a/app/common/renderer/lib/client-frameworks/robot.js b/app/common/renderer/lib/client-frameworks/robot.js
index 1223363a17..e44d3ad64b 100644
--- a/app/common/renderer/lib/client-frameworks/robot.js
+++ b/app/common/renderer/lib/client-frameworks/robot.js
@@ -136,15 +136,15 @@ ${varAssignment}Execute Script    ${scriptCmd}    $\{scriptArgument}`;
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return '${context} =    Get Current Context';
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return '${contexts} =    Get Contexts';
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `Switch To Context    ${name}`;
   }
 
diff --git a/app/common/renderer/lib/client-frameworks/ruby.js b/app/common/renderer/lib/client-frameworks/ruby.js
index f5a3e8e2e3..2eea5bd29e 100644
--- a/app/common/renderer/lib/client-frameworks/ruby.js
+++ b/app/common/renderer/lib/client-frameworks/ruby.js
@@ -150,15 +150,15 @@ driver.quit`;
 
   // Context
 
-  codeFor_getContext() {
+  codeFor_getAppiumContext() {
     return `context = driver.current_context`;
   }
 
-  codeFor_getContexts() {
+  codeFor_getAppiumContexts() {
     return `contexts = driver.available_contexts`;
   }
 
-  codeFor_switchContext(varNameIgnore, varIndexIgnore, name) {
+  codeFor_switchAppiumContext(varNameIgnore, varIndexIgnore, name) {
     return `driver.context = '${name}'`;
   }
 
diff --git a/docs/quickstart/requirements.md b/docs/quickstart/requirements.md
index 32ff211585..8975719c74 100644
--- a/docs/quickstart/requirements.md
+++ b/docs/quickstart/requirements.md
@@ -15,8 +15,7 @@ will differ:
     - Up to around **600MB** of free space is required
     - The minimum application window size is **890 x 710** pixels
 - Web app/Appium server plugin
-    - Works in Chrome/Edge/Firefox, released in 2022 or later
-      ([Safari is not supported](../troubleshooting.md#browser-version-does-not-work-in-safari))
+    - Works in Chrome/Edge/Firefox/Safari, released in 2022 or later
     - The plugin version requires around **9MB** of free space
     - Viewport size of at least **870 x 610** pixels is recommended
 
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 47bd5c18f0..22f1aea7d3 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -34,11 +34,6 @@ create a session.
     If you encounter this issue with a cloud provider, the fix must be applied on the provider side,
     because they are the ones hosting the Appium server.
 
-## Browser version does not work in Safari
-
-This is [a known issue](https://github.com/appium/appium-inspector/issues/103). Currently it is
-advised to use Chrome, Edge or Firefox instead.
-
 ## Start Session button is cut off
 
 This problem may appear if your computer screen size is too small. Check the
diff --git a/package-lock.json b/package-lock.json
index 0068fbd5eb..2ca6099e98 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
       "dependencies": {
         "@ant-design/pro-components": "2.8.9",
         "@reduxjs/toolkit": "2.8.2",
+        "@wdio/protocols": "9.16.2",
         "@xmldom/xmldom": "0.9.8",
         "antd": "5.26.4",
         "axios": "1.10.0",
@@ -32,7 +33,7 @@
         "react-icons": "5.5.0",
         "react-redux": "9.2.0",
         "react-router": "7.6.3",
-        "web2driver": "3.0.4",
+        "webdriver": "9.17.0",
         "xpath": "0.0.34"
       },
       "bin": {
@@ -3147,7 +3148,6 @@
       "version": "8.0.2",
       "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
       "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "string-width": "^5.1.2",
@@ -3165,7 +3165,6 @@
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
       "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=12"
@@ -3178,14 +3177,12 @@
       "version": "9.2.2",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/@isaacs/cliui/node_modules/string-width": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
       "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "eastasianwidth": "^0.2.0",
@@ -3203,7 +3200,6 @@
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
       "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^6.1.0",
@@ -3496,13 +3492,64 @@
       "version": "0.11.0",
       "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
       "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
-      "dev": true,
       "license": "MIT",
       "optional": true,
       "engines": {
         "node": ">=14"
       }
     },
+    "node_modules/@promptbook/utils": {
+      "version": "0.69.5",
+      "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.69.5.tgz",
+      "integrity": "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://buymeacoffee.com/hejny"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/webgptorg/promptbook/blob/main/README.md#%EF%B8%8F-contributing"
+        }
+      ],
+      "license": "CC-BY-4.0",
+      "dependencies": {
+        "spacetrim": "0.11.59"
+      }
+    },
+    "node_modules/@puppeteer/browsers": {
+      "version": "2.10.5",
+      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.5.tgz",
+      "integrity": "sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "debug": "^4.4.1",
+        "extract-zip": "^2.0.1",
+        "progress": "^2.0.3",
+        "proxy-agent": "^6.5.0",
+        "semver": "^7.7.2",
+        "tar-fs": "^3.0.8",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "browsers": "lib/cjs/main-cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@puppeteer/browsers/node_modules/semver": {
+      "version": "7.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+      "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@rc-component/async-validator": {
       "version": "5.0.4",
       "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
@@ -4055,6 +4102,12 @@
         "node": ">= 10"
       }
     },
+    "node_modules/@tootallnate/quickjs-emscripten": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+      "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
+      "license": "MIT"
+    },
     "node_modules/@tsconfig/node14": {
       "version": "14.1.3",
       "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.3.tgz",
@@ -4175,16 +4228,6 @@
         "@types/node": "*"
       }
     },
-    "node_modules/@types/glob": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz",
-      "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/minimatch": "^5.1.2",
-        "@types/node": "*"
-      }
-    },
     "node_modules/@types/http-cache-semantics": {
       "version": "4.0.4",
       "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
@@ -4222,12 +4265,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@types/minimatch": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
-      "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
-      "license": "MIT"
-    },
     "node_modules/@types/ms": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
@@ -4322,6 +4359,21 @@
       "license": "MIT",
       "optional": true
     },
+    "node_modules/@types/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/ws": {
+      "version": "8.18.1",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
+      "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/yauzl": {
       "version": "2.10.3",
       "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
@@ -4857,215 +4909,134 @@
       }
     },
     "node_modules/@wdio/config": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.40.0.tgz",
-      "integrity": "sha512-ayQELXyxa+k9/2a509F5a1oTsCa/w8D1nDrd+hzm+1mYb4Te2lceWCCzm+atGKkMpvjLH4GvhrEBYLh3rIWk2A==",
+      "version": "9.17.0",
+      "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.17.0.tgz",
+      "integrity": "sha512-AmP9J1GiCUj8g9aNeCuJRKAo+dET8rukVzrGQVq8jdqkj4PLjLA2hbfcQ8zQi/xwVUebnuIUT5weaomgKyb7Ng==",
       "license": "MIT",
       "dependencies": {
-        "@types/glob": "^8.1.0",
-        "@wdio/logger": "7.26.0",
-        "@wdio/types": "7.40.0",
-        "@wdio/utils": "7.40.0",
-        "deepmerge": "^4.0.0",
-        "glob": "^8.0.3"
+        "@wdio/logger": "9.16.2",
+        "@wdio/types": "9.16.2",
+        "@wdio/utils": "9.17.0",
+        "deepmerge-ts": "^7.0.3",
+        "glob": "^10.2.2",
+        "import-meta-resolve": "^4.0.0"
       },
       "engines": {
-        "node": ">=12.0.0"
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/@wdio/config/node_modules/@types/node": {
-      "version": "18.19.80",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz",
-      "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==",
+    "node_modules/@wdio/config/node_modules/@wdio/logger": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.16.2.tgz",
+      "integrity": "sha512-6A1eVpNPToWupLIo8CXStth4HJGTfxKsAiKtwE0xQFKyDM8uPTm3YO3Nf15vCSHbmsncbYVEo7QrUwRUEB4YUg==",
       "license": "MIT",
       "dependencies": {
-        "undici-types": "~5.26.4"
-      }
-    },
-    "node_modules/@wdio/config/node_modules/@wdio/types": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.40.0.tgz",
-      "integrity": "sha512-MWMbU+8uk+JrF7ygP/TJDsaSvFozKauiW6EnG7rxx9+GvU1Q1B3l4UjAc7GDbgLKjwt8T2y5GDRiDoD3UOjVyw==",
-      "license": "MIT",
-      "dependencies": {
-        "@types/node": "^18.0.0",
-        "got": "^11.8.1"
+        "chalk": "^5.1.2",
+        "loglevel": "^1.6.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "strip-ansi": "^7.1.0"
       },
       "engines": {
-        "node": ">=12.0.0"
-      },
-      "peerDependencies": {
-        "typescript": "^4.6.2"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/@wdio/config/node_modules/glob": {
-      "version": "8.1.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
-      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
-      "deprecated": "Glob versions prior to v9 are no longer supported",
-      "license": "ISC",
-      "dependencies": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^5.0.1",
-        "once": "^1.3.0"
-      },
+    "node_modules/@wdio/config/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
       "engines": {
-        "node": ">=12"
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
       },
       "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@wdio/config/node_modules/minimatch": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
-      "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
-      "license": "ISC",
-      "dependencies": {
-        "brace-expansion": "^2.0.1"
-      },
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/@wdio/config/node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
-      "license": "Apache-2.0",
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
-      "engines": {
-        "node": ">=4.2.0"
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "node_modules/@wdio/config/node_modules/undici-types": {
-      "version": "5.26.5",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+    "node_modules/@wdio/protocols": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.16.2.tgz",
+      "integrity": "sha512-h3k97/lzmyw5MowqceAuY3HX/wGJojXHkiPXA3WlhGPCaa2h4+GovV2nJtRvknCKsE7UHA1xB5SWeI8MzloBew==",
       "license": "MIT"
     },
-    "node_modules/@wdio/logger": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz",
-      "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==",
+    "node_modules/@wdio/types": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.16.2.tgz",
+      "integrity": "sha512-P86FvM/4XQGpJKwlC2RKF3I21TglPvPOozJGG9HoL0Jmt6jRF20ggO/nRTxU0XiWkRdqESUTmfA87bdCO4GRkQ==",
       "license": "MIT",
       "dependencies": {
-        "chalk": "^4.0.0",
-        "loglevel": "^1.6.0",
-        "loglevel-plugin-prefix": "^0.8.4",
-        "strip-ansi": "^6.0.0"
+        "@types/node": "^20.1.0"
       },
       "engines": {
-        "node": ">=12.0.0"
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/@wdio/logger/node_modules/ansi-regex": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8"
-      }
-    },
-    "node_modules/@wdio/logger/node_modules/strip-ansi": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+    "node_modules/@wdio/types/node_modules/@types/node": {
+      "version": "20.19.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz",
+      "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==",
       "license": "MIT",
       "dependencies": {
-        "ansi-regex": "^5.0.1"
-      },
-      "engines": {
-        "node": ">=8"
+        "undici-types": "~6.21.0"
       }
     },
-    "node_modules/@wdio/protocols": {
-      "version": "7.27.0",
-      "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-7.27.0.tgz",
-      "integrity": "sha512-hT/U22R5i3HhwPjkaKAG0yd59eaOaZB0eibRj2+esCImkb5Y6rg8FirrlYRxIGFVBl0+xZV0jKHzR5+o097nvg==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12.0.0"
-      }
+    "node_modules/@wdio/types/node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "license": "MIT"
     },
     "node_modules/@wdio/utils": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.40.0.tgz",
-      "integrity": "sha512-jLF57xHmz5nnGuM6ZRWjVYa/LQb22CS7yG50dUFa9wJ509mC1HlUzaA01Gjk9TV5jf9vnwE/yZfUMCoecTgG9w==",
+      "version": "9.17.0",
+      "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.17.0.tgz",
+      "integrity": "sha512-z9k8+DbhyJPLs88w/SLcUuXMdoPq2r/lE2qQ79pqWAG0jmm7kJqkBvZyndaTrSrGbh+NgjTEgh1epD1PHYtnwg==",
       "license": "MIT",
       "dependencies": {
-        "@wdio/logger": "7.26.0",
-        "@wdio/types": "7.40.0",
-        "p-iteration": "^1.1.8"
+        "@puppeteer/browsers": "^2.2.0",
+        "@wdio/logger": "9.16.2",
+        "@wdio/types": "9.16.2",
+        "decamelize": "^6.0.0",
+        "deepmerge-ts": "^7.0.3",
+        "edgedriver": "^6.1.1",
+        "geckodriver": "^5.0.0",
+        "get-port": "^7.0.0",
+        "import-meta-resolve": "^4.0.0",
+        "locate-app": "^2.2.24",
+        "mitt": "^3.0.1",
+        "safaridriver": "^1.0.0",
+        "split2": "^4.2.0",
+        "wait-port": "^1.1.0"
       },
       "engines": {
-        "node": ">=12.0.0"
-      }
-    },
-    "node_modules/@wdio/utils/node_modules/@types/node": {
-      "version": "18.19.80",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz",
-      "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==",
-      "license": "MIT",
-      "dependencies": {
-        "undici-types": "~5.26.4"
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/@wdio/utils/node_modules/@wdio/types": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.40.0.tgz",
-      "integrity": "sha512-MWMbU+8uk+JrF7ygP/TJDsaSvFozKauiW6EnG7rxx9+GvU1Q1B3l4UjAc7GDbgLKjwt8T2y5GDRiDoD3UOjVyw==",
+    "node_modules/@wdio/utils/node_modules/@wdio/logger": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.16.2.tgz",
+      "integrity": "sha512-6A1eVpNPToWupLIo8CXStth4HJGTfxKsAiKtwE0xQFKyDM8uPTm3YO3Nf15vCSHbmsncbYVEo7QrUwRUEB4YUg==",
       "license": "MIT",
       "dependencies": {
-        "@types/node": "^18.0.0",
-        "got": "^11.8.1"
+        "chalk": "^5.1.2",
+        "loglevel": "^1.6.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "strip-ansi": "^7.1.0"
       },
       "engines": {
-        "node": ">=12.0.0"
-      },
-      "peerDependencies": {
-        "typescript": "^4.6.2"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/@wdio/utils/node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
-      "license": "Apache-2.0",
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
-      },
+    "node_modules/@wdio/utils/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
       "engines": {
-        "node": ">=4.2.0"
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
-    "node_modules/@wdio/utils/node_modules/undici-types": {
-      "version": "5.26.5",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
-      "license": "MIT"
-    },
     "node_modules/@xmldom/xmldom": {
       "version": "0.9.8",
       "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz",
@@ -5075,6 +5046,17 @@
         "node": ">=14.6"
       }
     },
+    "node_modules/@zip.js/zip.js": {
+      "version": "2.7.63",
+      "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.63.tgz",
+      "integrity": "sha512-B02i6QDMUQ4c+5F9LmliBGA+jFsiEHIlF0eLQ6rWLaQOD3YwI6vyWwGkVCNJnVVguE2xYyr9fAwSD/3valm1/Q==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "bun": ">=0.7.0",
+        "deno": ">=1.0.0",
+        "node": ">=16.5.0"
+      }
+    },
     "node_modules/7zip-bin": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
@@ -5153,7 +5135,6 @@
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
       "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 14"
@@ -5227,7 +5208,6 @@
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
       "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=12"
@@ -5786,6 +5766,18 @@
         "node": ">=12"
       }
     },
+    "node_modules/ast-types": {
+      "version": "0.13.4",
+      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+      "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/astral-regex": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@@ -5894,7 +5886,6 @@
       "version": "1.6.7",
       "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
       "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
-      "dev": true,
       "license": "Apache-2.0"
     },
     "node_modules/balanced-match": {
@@ -5907,10 +5898,74 @@
       "version": "2.5.4",
       "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
       "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
-      "dev": true,
       "license": "Apache-2.0",
       "optional": true
     },
+    "node_modules/bare-fs": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz",
+      "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "bare-events": "^2.5.4",
+        "bare-path": "^3.0.0",
+        "bare-stream": "^2.6.4"
+      },
+      "engines": {
+        "bare": ">=1.16.0"
+      },
+      "peerDependencies": {
+        "bare-buffer": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-buffer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/bare-os": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz",
+      "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "engines": {
+        "bare": ">=1.14.0"
+      }
+    },
+    "node_modules/bare-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
+      "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "bare-os": "^3.0.1"
+      }
+    },
+    "node_modules/bare-stream": {
+      "version": "2.6.5",
+      "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz",
+      "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "streamx": "^2.21.0"
+      },
+      "peerDependencies": {
+        "bare-buffer": "*",
+        "bare-events": "*"
+      },
+      "peerDependenciesMeta": {
+        "bare-buffer": {
+          "optional": true
+        },
+        "bare-events": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/base64-js": {
       "version": "1.5.1",
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -5961,6 +6016,15 @@
       "license": "MIT",
       "peer": true
     },
+    "node_modules/basic-ftp": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
+      "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/big-integer": {
       "version": "1.6.52",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
@@ -6749,7 +6813,6 @@
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
       "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "string-width": "^4.2.0",
@@ -6764,7 +6827,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -6774,7 +6836,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -7131,7 +7192,6 @@
       "version": "7.0.6",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
       "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "path-key": "^3.1.0",
@@ -7146,14 +7206,12 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true,
       "license": "ISC"
     },
     "node_modules/cross-spawn/node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "isexe": "^2.0.0"
@@ -7199,6 +7257,15 @@
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
       "license": "MIT"
     },
+    "node_modules/data-uri-to-buffer": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
+      "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/data-view-buffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@@ -7276,6 +7343,18 @@
         }
       }
     },
+    "node_modules/decamelize": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz",
+      "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/decompress-response": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -7320,13 +7399,13 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/deepmerge": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
-      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
-      "license": "MIT",
+    "node_modules/deepmerge-ts": {
+      "version": "7.1.5",
+      "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz",
+      "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==",
+      "license": "BSD-3-Clause",
       "engines": {
-        "node": ">=0.10.0"
+        "node": ">=16.0.0"
       }
     },
     "node_modules/defaults": {
@@ -7387,6 +7466,20 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/degenerator": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+      "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ast-types": "^0.13.4",
+        "escodegen": "^2.1.0",
+        "esprima": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -7734,9 +7827,138 @@
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
       "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
-      "dev": true,
       "license": "MIT"
     },
+    "node_modules/edge-paths": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz",
+      "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/which": "^2.0.1",
+        "which": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/shirshak55"
+      }
+    },
+    "node_modules/edge-paths/node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC"
+    },
+    "node_modules/edge-paths/node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/edgedriver": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.1.1.tgz",
+      "integrity": "sha512-/dM/PoBf22Xg3yypMWkmRQrBKEnSyNaZ7wHGCT9+qqT14izwtFT+QvdR89rjNkMfXwW+bSFoqOfbcvM+2Cyc7w==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "@wdio/logger": "^9.1.3",
+        "@zip.js/zip.js": "^2.7.53",
+        "decamelize": "^6.0.0",
+        "edge-paths": "^3.0.5",
+        "fast-xml-parser": "^4.5.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.5",
+        "node-fetch": "^3.3.2",
+        "which": "^5.0.0"
+      },
+      "bin": {
+        "edgedriver": "bin/edgedriver.js"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/edgedriver/node_modules/@wdio/logger": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.16.2.tgz",
+      "integrity": "sha512-6A1eVpNPToWupLIo8CXStth4HJGTfxKsAiKtwE0xQFKyDM8uPTm3YO3Nf15vCSHbmsncbYVEo7QrUwRUEB4YUg==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^5.1.2",
+        "loglevel": "^1.6.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18.20.0"
+      }
+    },
+    "node_modules/edgedriver/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/edgedriver/node_modules/data-uri-to-buffer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/edgedriver/node_modules/node-fetch": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/edgedriver/node_modules/which": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+      "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -8111,7 +8333,6 @@
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/enabled": {
@@ -8472,7 +8693,6 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
       "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -8499,6 +8719,27 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/escodegen": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+      "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^5.2.0",
+        "esutils": "^2.0.2"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
     "node_modules/eslint": {
       "version": "9.31.0",
       "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz",
@@ -9052,6 +9293,19 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "license": "BSD-2-Clause",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/esquery": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
@@ -9082,7 +9336,6 @@
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
       "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=4.0"
@@ -9102,7 +9355,6 @@
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
-      "dev": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=0.10.0"
@@ -9307,7 +9559,6 @@
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
       "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/fast-glob": {
@@ -9374,6 +9625,24 @@
       "license": "BSD-3-Clause",
       "peer": true
     },
+    "node_modules/fast-xml-parser": {
+      "version": "4.5.3",
+      "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
+      "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "strnum": "^1.1.1"
+      },
+      "bin": {
+        "fxparser": "src/cli/cli.js"
+      }
+    },
     "node_modules/fastest-levenshtein": {
       "version": "1.0.16",
       "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@@ -9428,6 +9697,29 @@
       "license": "MIT",
       "peer": true
     },
+    "node_modules/fetch-blob": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+      "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      },
+      "engines": {
+        "node": "^12.20 || >= 14.13"
+      }
+    },
     "node_modules/file-entry-cache": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -9603,7 +9895,6 @@
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
       "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "cross-spawn": "^7.0.6",
@@ -9631,6 +9922,18 @@
         "node": ">= 6"
       }
     },
+    "node_modules/formdata-polyfill": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+      "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+      "license": "MIT",
+      "dependencies": {
+        "fetch-blob": "^3.1.2"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -9697,6 +10000,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
       "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true,
       "license": "ISC"
     },
     "node_modules/fsevents": {
@@ -9793,6 +10097,98 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/geckodriver": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-5.0.0.tgz",
+      "integrity": "sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "@wdio/logger": "^9.1.3",
+        "@zip.js/zip.js": "^2.7.53",
+        "decamelize": "^6.0.0",
+        "http-proxy-agent": "^7.0.2",
+        "https-proxy-agent": "^7.0.5",
+        "node-fetch": "^3.3.2",
+        "tar-fs": "^3.0.6",
+        "which": "^5.0.0"
+      },
+      "bin": {
+        "geckodriver": "bin/geckodriver.js"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/geckodriver/node_modules/@wdio/logger": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.16.2.tgz",
+      "integrity": "sha512-6A1eVpNPToWupLIo8CXStth4HJGTfxKsAiKtwE0xQFKyDM8uPTm3YO3Nf15vCSHbmsncbYVEo7QrUwRUEB4YUg==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^5.1.2",
+        "loglevel": "^1.6.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18.20.0"
+      }
+    },
+    "node_modules/geckodriver/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/geckodriver/node_modules/data-uri-to-buffer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/geckodriver/node_modules/node-fetch": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/geckodriver/node_modules/which": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+      "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^3.1.1"
+      },
+      "bin": {
+        "node-which": "bin/which.js"
+      },
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -9807,7 +10203,6 @@
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": "6.* || 8.* || >= 10.*"
@@ -9837,6 +10232,18 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/get-port": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
+      "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-proto": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
@@ -9895,11 +10302,24 @@
         "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
       }
     },
+    "node_modules/get-uri": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+      "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
+      "license": "MIT",
+      "dependencies": {
+        "basic-ftp": "^5.0.2",
+        "data-uri-to-buffer": "^6.0.2",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/glob": {
       "version": "10.4.5",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
       "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "foreground-child": "^3.1.0",
@@ -9933,7 +10353,6 @@
       "version": "9.0.5",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
       "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
-      "dev": true,
       "license": "ISC",
       "dependencies": {
         "brace-expansion": "^2.0.1"
@@ -10318,7 +10737,6 @@
       "version": "7.0.2",
       "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
       "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "agent-base": "^7.1.0",
@@ -10353,7 +10771,6 @@
       "version": "7.0.6",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
       "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "agent-base": "^7.1.2",
@@ -10555,8 +10972,18 @@
       "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
       "dev": true,
       "license": "MIT",
-      "engines": {
-        "node": ">=4"
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/import-meta-resolve": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+      "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
       }
     },
     "node_modules/imurmurhash": {
@@ -10590,6 +11017,7 @@
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
       "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "dev": true,
       "license": "ISC",
       "dependencies": {
         "once": "^1.3.0",
@@ -10600,6 +11028,7 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true,
       "license": "ISC"
     },
     "node_modules/internal-slot": {
@@ -10621,7 +11050,6 @@
       "version": "9.0.5",
       "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
       "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "jsbn": "1.1.0",
@@ -10838,7 +11266,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
       "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -11144,7 +11571,6 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
       "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=16"
@@ -11172,7 +11598,6 @@
       "version": "3.4.3",
       "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
       "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
-      "dev": true,
       "license": "BlueOak-1.0.0",
       "dependencies": {
         "@isaacs/cliui": "^8.0.2"
@@ -11249,7 +11674,6 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
       "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/jsesc": {
@@ -11485,18 +11909,6 @@
       "license": "MIT",
       "peer": true
     },
-    "node_modules/ky": {
-      "version": "0.30.0",
-      "resolved": "https://registry.npmjs.org/ky/-/ky-0.30.0.tgz",
-      "integrity": "sha512-X/u76z4JtDVq10u1JA5UQfatPxgPaVDMYTrgHyiTpGN2z4TMEJkIHsoSBBSg9SWZEIXTKsi9kHgiQ9o3Y/4yog==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sindresorhus/ky?sponsor=1"
-      }
-    },
     "node_modules/lazy-val": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
@@ -11644,6 +12056,39 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/locate-app": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.5.0.tgz",
+      "integrity": "sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://buymeacoffee.com/hejny"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/hejny/locate-app/blob/main/README.md#%EF%B8%8F-contributing"
+        }
+      ],
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@promptbook/utils": "0.69.5",
+        "type-fest": "4.26.0",
+        "userhome": "1.0.1"
+      }
+    },
+    "node_modules/locate-app/node_modules/type-fest": {
+      "version": "4.26.0",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz",
+      "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==",
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -11714,6 +12159,7 @@
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/log-symbols": {
@@ -11803,7 +12249,6 @@
       "version": "10.4.3",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
       "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
-      "dev": true,
       "license": "ISC"
     },
     "node_modules/magic-string": {
@@ -12167,7 +12612,6 @@
       "version": "7.1.2",
       "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
       "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=16 || 14 >=14.17"
@@ -12335,6 +12779,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+      "license": "MIT"
+    },
     "node_modules/mkdirp": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@@ -12577,6 +13027,15 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/netmask": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+      "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/node-abi": {
       "version": "3.74.0",
       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz",
@@ -12608,6 +13067,26 @@
         "semver": "^7.3.5"
       }
     },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+      "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+      "deprecated": "Use your platform's native DOMException instead",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
     "node_modules/node-fetch": {
       "version": "2.7.0",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -13000,15 +13479,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/p-iteration": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.8.tgz",
-      "integrity": "sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
     "node_modules/p-limit": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -13057,6 +13527,52 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/pac-proxy-agent": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
+      "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
+      "license": "MIT",
+      "dependencies": {
+        "@tootallnate/quickjs-emscripten": "^0.23.0",
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "get-uri": "^6.0.1",
+        "http-proxy-agent": "^7.0.0",
+        "https-proxy-agent": "^7.0.6",
+        "pac-resolver": "^7.0.1",
+        "socks-proxy-agent": "^8.0.5"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+      "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "socks": "^2.8.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/pac-resolver": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
+      "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
+      "license": "MIT",
+      "dependencies": {
+        "degenerator": "^5.0.0",
+        "netmask": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/package-changed": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/package-changed/-/package-changed-3.0.0.tgz",
@@ -13086,7 +13602,6 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
       "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
-      "dev": true,
       "license": "BlueOak-1.0.0"
     },
     "node_modules/pako": {
@@ -13233,7 +13748,6 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
       "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -13250,7 +13764,6 @@
       "version": "1.11.1",
       "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
       "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
-      "dev": true,
       "license": "BlueOak-1.0.0",
       "dependencies": {
         "lru-cache": "^10.2.0",
@@ -13499,6 +14012,7 @@
       "version": "0.11.10",
       "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
       "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.6.0"
@@ -13567,6 +14081,48 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-agent": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+      "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "http-proxy-agent": "^7.0.1",
+        "https-proxy-agent": "^7.0.6",
+        "lru-cache": "^7.14.1",
+        "pac-proxy-agent": "^7.1.0",
+        "proxy-from-env": "^1.1.0",
+        "socks-proxy-agent": "^8.0.5"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/proxy-agent/node_modules/lru-cache": {
+      "version": "7.18.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/proxy-agent/node_modules/socks-proxy-agent": {
+      "version": "8.0.5",
+      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+      "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "^4.3.4",
+        "socks": "^2.8.3"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -14613,7 +15169,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -14957,6 +15512,15 @@
         "queue-microtask": "^1.2.2"
       }
     },
+    "node_modules/safaridriver": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.0.tgz",
+      "integrity": "sha512-J92IFbskyo7OYB3Dt4aTdyhag1GlInrfbPCmMteb7aBK7PwlnGz1HI0+oyNN97j7pV9DqUAVoVgkNRMrfY47mQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
     "node_modules/safe-array-concat": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@@ -15404,7 +15968,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
       "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "shebang-regex": "^3.0.0"
@@ -15417,7 +15980,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -15523,7 +16085,6 @@
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
       "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=14"
@@ -15582,7 +16143,6 @@
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
       "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 6.0.0",
@@ -15593,7 +16153,6 @@
       "version": "2.8.4",
       "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
       "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ip-address": "^9.0.5",
@@ -15636,7 +16195,7 @@
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
+      "devOptional": true,
       "license": "BSD-3-Clause",
       "engines": {
         "node": ">=0.10.0"
@@ -15663,6 +16222,22 @@
         "source-map": "^0.6.0"
       }
     },
+    "node_modules/spacetrim": {
+      "version": "0.11.59",
+      "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz",
+      "integrity": "sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://buymeacoffee.com/hejny"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/hejny/spacetrim/blob/main/README.md#%EF%B8%8F-contributing"
+        }
+      ],
+      "license": "Apache-2.0"
+    },
     "node_modules/spdx-correct": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
@@ -15752,11 +16327,19 @@
         "node": ">= 6"
       }
     },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
     "node_modules/sprintf-js": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
       "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
-      "devOptional": true,
       "license": "BSD-3-Clause"
     },
     "node_modules/ssri": {
@@ -15864,7 +16447,6 @@
       "version": "2.22.0",
       "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz",
       "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "fast-fifo": "^1.3.2",
@@ -15894,7 +16476,6 @@
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "emoji-regex": "^8.0.0",
@@ -15910,7 +16491,6 @@
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
       "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "emoji-regex": "^8.0.0",
@@ -15925,7 +16505,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -15935,7 +16514,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -15948,7 +16526,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -15958,7 +16535,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -16069,7 +16645,6 @@
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
       "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^6.0.1"
@@ -16086,7 +16661,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -16099,7 +16673,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -16149,6 +16722,18 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/strnum": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
+      "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/NaturalIntelligence"
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/stylis": {
       "version": "4.3.6",
       "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
@@ -16227,11 +16812,24 @@
         "node": ">=10"
       }
     },
+    "node_modules/tar-fs": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz",
+      "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==",
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0",
+        "tar-stream": "^3.1.5"
+      },
+      "optionalDependencies": {
+        "bare-fs": "^4.0.1",
+        "bare-path": "^3.0.0"
+      }
+    },
     "node_modules/tar-stream": {
       "version": "3.1.7",
       "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
       "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "b4a": "^1.6.4",
@@ -16413,7 +17011,6 @@
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
       "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
-      "dev": true,
       "license": "Apache-2.0",
       "dependencies": {
         "b4a": "^1.6.4"
@@ -17031,6 +17628,15 @@
         "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
       }
     },
+    "node_modules/userhome": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz",
+      "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
     "node_modules/utf8-byte-length": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
@@ -17303,6 +17909,32 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/wait-port": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz",
+      "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2",
+        "commander": "^9.3.0",
+        "debug": "^4.3.4"
+      },
+      "bin": {
+        "wait-port": "bin/wait-port.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/wait-port/node_modules/commander": {
+      "version": "9.5.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+      "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || >=14"
+      }
+    },
     "node_modules/warning": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
@@ -17334,88 +17966,86 @@
         "defaults": "^1.0.3"
       }
     },
-    "node_modules/web2driver": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/web2driver/-/web2driver-3.0.4.tgz",
-      "integrity": "sha512-2jqJzH+1vmgfWBs9tmREsLlBAhWQUpMtKQJUdRNq4OXmmTWrdf8iyh21ifdalhL/5mNpdQ28QkFujoc4lCA5XQ==",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@wdio/logger": "^7.7.0",
-        "@wdio/protocols": "^7.7.4",
-        "lodash": "^4.17.11",
-        "process": "^0.11.10",
-        "webdriver": "^7.16.14"
+    "node_modules/web-streams-polyfill": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+      "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
       }
     },
     "node_modules/webdriver": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.40.0.tgz",
-      "integrity": "sha512-CKi3cDWgNVE/ibcsBfdtA+pQVeZ4oYlecLlwemulVxJdgr4l5bv+nXuoIhnYeVb6aAI4naK772vmWQ0XuRYhDQ==",
+      "version": "9.17.0",
+      "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.17.0.tgz",
+      "integrity": "sha512-vDdW5SeAe1Fn2N79cdrRIHnbTcXlpIM1f0Ki8AHGsC0DkvYRyshnuI6kgySXN8y4+Thy/xo+vDdr0CLJF2Zqgw==",
       "license": "MIT",
       "dependencies": {
-        "@types/node": "^18.0.0",
-        "@wdio/config": "7.40.0",
-        "@wdio/logger": "7.26.0",
-        "@wdio/protocols": "7.27.0",
-        "@wdio/types": "7.40.0",
-        "@wdio/utils": "7.40.0",
-        "got": "^11.0.2",
-        "ky": "0.30.0",
-        "lodash.merge": "^4.6.1"
+        "@types/node": "^20.1.0",
+        "@types/ws": "^8.5.3",
+        "@wdio/config": "9.17.0",
+        "@wdio/logger": "9.16.2",
+        "@wdio/protocols": "9.16.2",
+        "@wdio/types": "9.16.2",
+        "@wdio/utils": "9.17.0",
+        "deepmerge-ts": "^7.0.3",
+        "https-proxy-agent": "^7.0.6",
+        "undici": "^6.20.1",
+        "ws": "^8.8.0"
       },
       "engines": {
-        "node": ">=12.0.0"
+        "node": ">=18.20.0"
       }
     },
     "node_modules/webdriver/node_modules/@types/node": {
-      "version": "18.19.80",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz",
-      "integrity": "sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==",
+      "version": "20.19.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz",
+      "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==",
       "license": "MIT",
       "dependencies": {
-        "undici-types": "~5.26.4"
+        "undici-types": "~6.21.0"
       }
     },
-    "node_modules/webdriver/node_modules/@wdio/types": {
-      "version": "7.40.0",
-      "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.40.0.tgz",
-      "integrity": "sha512-MWMbU+8uk+JrF7ygP/TJDsaSvFozKauiW6EnG7rxx9+GvU1Q1B3l4UjAc7GDbgLKjwt8T2y5GDRiDoD3UOjVyw==",
+    "node_modules/webdriver/node_modules/@wdio/logger": {
+      "version": "9.16.2",
+      "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.16.2.tgz",
+      "integrity": "sha512-6A1eVpNPToWupLIo8CXStth4HJGTfxKsAiKtwE0xQFKyDM8uPTm3YO3Nf15vCSHbmsncbYVEo7QrUwRUEB4YUg==",
       "license": "MIT",
       "dependencies": {
-        "@types/node": "^18.0.0",
-        "got": "^11.8.1"
+        "chalk": "^5.1.2",
+        "loglevel": "^1.6.0",
+        "loglevel-plugin-prefix": "^0.8.4",
+        "strip-ansi": "^7.1.0"
       },
       "engines": {
-        "node": ">=12.0.0"
-      },
-      "peerDependencies": {
-        "typescript": "^4.6.2"
-      },
-      "peerDependenciesMeta": {
-        "typescript": {
-          "optional": true
-        }
+        "node": ">=18.20.0"
       }
     },
-    "node_modules/webdriver/node_modules/typescript": {
-      "version": "4.9.5",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-      "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
-      "license": "Apache-2.0",
-      "optional": true,
-      "peer": true,
-      "bin": {
-        "tsc": "bin/tsc",
-        "tsserver": "bin/tsserver"
+    "node_modules/webdriver/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
       },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/webdriver/node_modules/undici": {
+      "version": "6.21.3",
+      "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz",
+      "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==",
+      "license": "MIT",
       "engines": {
-        "node": ">=4.2.0"
+        "node": ">=18.17"
       }
     },
     "node_modules/webdriver/node_modules/undici-types": {
-      "version": "5.26.5",
-      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
-      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "version": "6.21.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
       "license": "MIT"
     },
     "node_modules/webidl-conversions": {
@@ -17675,7 +18305,6 @@
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
       "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.0.0",
@@ -17694,7 +18323,6 @@
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
       "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.0.0",
@@ -17712,7 +18340,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -17722,7 +18349,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -17735,7 +18361,6 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
       "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -17745,7 +18370,6 @@
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
       "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-regex": "^5.0.1"
@@ -17782,9 +18406,7 @@
       "version": "8.18.1",
       "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
       "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
-      "dev": true,
       "license": "MIT",
-      "peer": true,
       "engines": {
         "node": ">=10.0.0"
       },
@@ -17824,7 +18446,6 @@
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
       "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=10"
@@ -17862,7 +18483,6 @@
       "version": "17.7.2",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
       "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "cliui": "^8.0.1",
@@ -17881,7 +18501,6 @@
       "version": "21.1.1",
       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
       "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
-      "dev": true,
       "license": "ISC",
       "engines": {
         "node": ">=12"
diff --git a/package.json b/package.json
index 32875f00f5..77cc4744ed 100644
--- a/package.json
+++ b/package.json
@@ -64,12 +64,12 @@
     "electron": "./node_modules/.bin/electron"
   },
   "//dependencies": {
-    "react": "V19: requires antd 5",
-    "web2driver": "3.0.5: does not load"
+    "react": "V19: requires antd 5"
   },
   "dependencies": {
     "@ant-design/pro-components": "2.8.9",
     "@reduxjs/toolkit": "2.8.2",
+    "@wdio/protocols": "9.16.2",
     "@xmldom/xmldom": "0.9.8",
     "antd": "5.26.4",
     "axios": "1.10.0",
@@ -91,7 +91,7 @@
     "react-icons": "5.5.0",
     "react-redux": "9.2.0",
     "react-router": "7.6.3",
-    "web2driver": "3.0.4",
+    "webdriver": "9.17.0",
     "xpath": "0.0.34"
   },
   "devDependencies": {
diff --git a/renovate.json b/renovate.json
index 0c23156ec6..be0e9d4b35 100644
--- a/renovate.json
+++ b/renovate.json
@@ -17,9 +17,9 @@
       "enabled": false
     },
     {
-      "matchPackageNames": ["web2driver"],
-      "matchUpdateTypes": ["minor", "patch"],
-      "enabled": false
+      "matchPackageNames": ["webdriver", "@wdio/protocols"],
+      "groupName": "WebdriverIO-related packages",
+      "groupSlug": "webdriverio"
     },
     {
       "matchPackageNames": ["appium", "@appium/**", "!@appium/base-plugin"],
diff --git a/vite.config.mjs b/vite.config.mjs
index e44e66d0e7..7e09ada5da 100644
--- a/vite.config.mjs
+++ b/vite.config.mjs
@@ -3,28 +3,35 @@ import {join} from 'path';
 import {defineConfig} from 'vite';
 
 // https://vitejs.dev/config/
-export default defineConfig({
-  build: {
-    outDir: join(__dirname, 'dist-browser'),
-    emptyOutDir: true,
-    minify: false, // needed to avoid issues with web2driver
-    reportCompressedSize: false,
-    target: 'es2022',
-  },
-  define: {
-    // add empty polyfills for some Node.js primitives
-    'process.argv': [],
-    'process.env': {},
-  },
-  plugins: [react()],
-  resolve: {
-    alias: {
-      '#local-polyfills': join(__dirname, 'app', 'web', 'polyfills'),
+export default defineConfig(({command}) => {
+  const commonConfig = {
+    build: {
+      outDir: join(__dirname, 'dist-browser'),
+      emptyOutDir: true,
+      minify: false,
+      reportCompressedSize: false,
+      target: 'es2022',
     },
-  },
-  root: join(__dirname, 'app', 'common'),
-  test: {
-    restoreMocks: true,
-    root: join(__dirname, 'test'),
-  },
+    define: {
+      // add empty polyfills for some Node.js primitives
+      'process.argv': [],
+      'process.env': {},
+    },
+    plugins: [react()],
+    resolve: {
+      alias: {
+        '#local-polyfills': join(__dirname, 'app', 'web', 'polyfills'),
+      },
+    },
+    root: join(__dirname, 'app', 'common'),
+    test: {
+      restoreMocks: true,
+      root: join(__dirname, 'test'),
+    },
+  };
+  // workaround to prevent webdriver from bundling various Node.js imports
+  if (command === 'build') {
+    commonConfig.define = {...commonConfig.define, 'globalThis.window': true};
+  }
+  return commonConfig;
 });


Test patch:
diff --git a/test/integration/client-actions.test.js b/test/integration/client-actions.test.js
index 469d6e2aef..30f43def92 100644
--- a/test/integration/client-actions.test.js
+++ b/test/integration/client-actions.test.js
@@ -1,8 +1,8 @@
 import {startServer as startAppiumFakeDriverServer} from '@appium/fake-driver';
 import path from 'path';
 import {afterAll, beforeAll, describe, expect, it} from 'vitest';
-import {Web2Driver} from 'web2driver/node';
 
+import Web2Driver from '../../app/common/renderer/lib/appium/driver';
 import InspectorDriver from '../../app/common/renderer/lib/appium/inspector-driver';
 
 const FAKE_DRIVER_PORT = 12121;