Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
94248cd
refactor(elements): produce the element snapshot with @wdio/snapshot
Winify Oct 19, 2026
ea28ce5
fix: ship the show-trace bin each adapter declares
Winify Oct 12, 2026
4a96050
feat(service): require WebdriverIO 10 (reverted by "chore: accept Web…
Winify Oct 19, 2026
04d3344
docs(core): fix a stale source-XML comment
Winify Oct 19, 2026
b061227
fix(elements): accept any @wdio/snapshot 10 minor
Winify Oct 19, 2026
8e42f12
docs(elements): drop references to the removed snapshot API
Winify Oct 19, 2026
b685af0
docs(changeset): name the snapshot routing and getElements output cha…
Winify Oct 20, 2026
ca10e9f
fix(elements): detect native sessions from vendor capabilities
Winify Oct 20, 2026
81a2e0b
test(conformance): answer GET /timeouts the way a real browser does
Winify Oct 20, 2026
2c2728c
chore(deps): move the workspace to WebdriverIO 10.1.0
Winify Oct 20, 2026
f0483bf
test(conformance): answer only a GET of /timeouts with the defaults
Winify Oct 20, 2026
7f44516
chore(changeset): note the WebdriverIO 10.1.0 runtime lines
Winify Oct 20, 2026
259d11a
fix(elements): stop retrying classic executeScript once it has failed
Winify Oct 27, 2026
abe47e4
feat(elements): keep the current snapshot renderer below WebdriverIO …
Winify Nov 2, 2026
22e609a
feat(service): return the snapshot result format on every WebdriverIO…
Winify Nov 2, 2026
4a553e0
chore: accept WebdriverIO 9 again
Winify Nov 2, 2026
175eaad
test(ci): check the WebdriverIO 9 peer leg picks the legacy renderer
Winify Nov 2, 2026
d7f5423
docs: describe how the installed WebdriverIO picks the snapshot renderer
Winify Nov 2, 2026
4303c05
docs: describe @wdio/snapshot as the CLI's renderer, not its output
Winify Nov 2, 2026
2f7ca3c
feat(elements): make @wdio/snapshot an opt-in peer
Winify Nov 2, 2026
474a676
fix(elements): gate the snapshot renderer on @wdio/snapshot 10.2 and …
Winify Nov 2, 2026
3d4c594
chore(deps): verify against WebdriverIO 10.2.0-alpha (drop before merge)
Winify Nov 2, 2026
f1086be
fix(elements): address review findings on the snapshot renderer
Winify Nov 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/show-trace-bin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@wdio/devtools-service': patch
'@wdio/selenium-devtools': patch
'@wdio/nightwatch-devtools': patch
---

Ship the `show-trace` bin the package has always declared. The file was never committed, so `npx show-trace <trace.zip>` failed in a project that installed only this adapter.
6 changes: 6 additions & 0 deletions .changeset/snapshot-matches-the-wdio-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@wdio/elements": minor
"@wdio/devtools-service": minor
---

`getSnapshot` can now render with `@wdio/snapshot`, the renderer behind `wdio session snapshot --selectors`. It is opt-in: on WebdriverIO 10.2 (prereleases included) with `@wdio/snapshot` 10.2 or later resolvable (`npm i -D @wdio/snapshot`), it is used; otherwise, including on WebdriverIO 9 and 10.0–10.1, the current output is kept. The result gains `format: 'legacy' | 'snapshot'`, and on the snapshot path `refs`; `elements` is filled on both paths. `viewport` is accepted next to `inViewportOnly`, and wins when both are set. `viewport` defaults to viewport-only on both renderers. `browser.getSnapshot()` in the service defaults to the viewport on both. Existing code keeps compiling.
7 changes: 7 additions & 0 deletions .changeset/wdio-10-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wdio/devtools-backend": patch
"@wdio/selenium-devtools": patch
"@wdio/nightwatch-devtools": patch
---

The `@wdio/logger` each package depends on, and the `webdriverio` the Selenium and Nightwatch adapters depend on, move to their 10.1.0 lines (`^10.1.0`), alongside the rest of the WebdriverIO toolchain the repository now builds and tests against. An install that pinned either to 10.0.x resolves 10.1.x from now on.
6 changes: 6 additions & 0 deletions .github/scripts/wdio-peer-smoke/check-imports.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ async function checkEntryPoints() {
const elements = await import('@wdio/elements')
for (const fn of [
'getSnapshot',
'snapshotRenderer',
'getElements',
'getBrowserAccessibilityTree'
]) {
assert.equal(typeof elements[fn], 'function', `@wdio/elements ${fn}`)
}
assert.equal(
elements.snapshotRenderer(),
'legacy',
'no @wdio/snapshot is installed here, so the legacy renderer renders'
)
const locators = await import('@wdio/elements/locators')
assert.equal(typeof locators.xmlToJSON, 'function', '@wdio/elements/locators')
}
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ coverage/

# pnpm state, cache, logs, and debug files
/packages/**/*.mjs
!/packages/*/bin/*.mjs

.claude

Expand All @@ -73,4 +74,3 @@ examples/**/allure-report*/
packages/**/allure-results*/
packages/**/allure-report*/
examples/wdio/cucumber/wdio.allure.conf.ts

2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ The execution environment is the browser, not Node, so this package cannot impor

The WDIO-typed element-detection API (`@wdio/elements`): `getSnapshot`, `getElements`, the accessibility-tree and interactable-element readers. Unlike `shared` and `core` this package **is** published, so its exported names are public API.

Imports from: `core`. The script bodies, serializers, types, and locator generation all live in `core` (`element-scripts.ts`, `element-snapshot.ts`, `element-types.ts`, `locators/`) because they're framework-agnostic; this package holds only the wrappers that take a `WebdriverIO.Browser` — which `core` can't type against. The arrow never reverses: `core` does not import `elements`, and the adapters' per-action capture (`captureActionSnapshot`) calls core's script bodies directly rather than going through this package. `elements/snapshot.ts` and `elements/locators/index.ts` are re-export shims kept for API stability.
Imports from: `core`. The script bodies, serializers, types, and locator generation all live in `core` (`element-scripts.ts`, `element-snapshot.ts`, `element-types.ts`, `locators/`) because they're framework-agnostic; this package holds only the wrappers that take a `WebdriverIO.Browser` — which `core` can't type against. The arrow never reverses: `core` does not import `elements`, and the adapters' per-action capture (`captureActionSnapshot`) calls core's script bodies directly rather than going through this package. `elements/locators/index.ts` is a re-export shim kept for API stability.

The DOM-walking scripts run in the page via `browser.execute`, so — like `script` — they avoid Node-only APIs.

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ Documented divergences from the conventions above. They exist today as debt to b
- Relatedly, cucumber's **per-step** `resetCommandTracking()` was wiping the native-assert buffer, so each scenario kept only its *last* step's assertions (measured 1 of 2). The buffer is per **test unit** — `resetTestTracking()` at `wrapBrowserOnce` now clears it.
- **DOM `mutations` ARE captured**, and the old `ECONNRESET` / "collector not found" attribution is **wrong** — neither appears any more. The gap closed itself with document-start injection (`core/bidi-preload.ts`), confirmed on a *baseline* build so the credit belongs to that change and not to anything here. Measured per scenario with `traceGranularity:'test'` + `webSocketUrl: true`: 39 and 19 mutation entries, 2 DOM anchors each, **0 of 19 rows on the wrong document**. What remains is noise, not a gap: the screencast poller issues `/screenshot` every 200 ms into the session Nightwatch quits per scenario, logging 9–26 `WARN webdriverHttp: … socket hang up` per run. Fix is to stop the recorder before the per-scenario quit, or suppress the warn for a session in teardown.
- **A tsup entry that another entry imports is not a leaf, so an `import.meta.url === process.argv[1]` self-start check inside it is dead code; a CLI has to be its own leaf entry.** tsup hoists a module body shared by two entries into `dist/chunk-*.js`, and there `import.meta.url` is the chunk's path, which can never equal `process.argv[1]`. `packages/backend/src/show-trace.ts` imports `start` from `index.ts`, which is what made index shared, so index's old "start if run directly" guard was dead in **every** build: `node dist/index.js` exited 0 without ever serving, while `dist/show-trace.js` self-started correctly for exactly the same reason inverted, being a leaf whose body stays in its own output file (it also compares realpaths, because the invoked path is the `node_modules/.bin` symlink). The live dashboard server is therefore its own leaf entry, `packages/backend/src/server.ts` (shebang, built to an executable `dist/server.js`, shipped as the `devtools-backend` bin, accepting `--port`, `--hostname`, `-h`/`--help`), and `index.ts` stays library-only for the three adapters' in-process `start`/`stop`. Same family as the `cucumberHooks.cts` entry above: the tsup entry list is part of the contract, and in both cases the symptom was silence rather than an error.
- **A captured text locator is generated in the recording runner's dialect; every other branch is portable CSS.** `shared/locator-dialect.ts` `locatorDialect(runner)` is the one fact table — WDIO runners (`mocha`/`jasmine`/`cucumber`) get `a*=Logout`, `nightwatch*`/`selenium-webdriver` and an **unidentified recorder** get `//a[contains(., "Logout")]`. The id reaches the page script as `CaptureActionSnapshotInput.runner` and the zip as an extension field on `context-options`, read back through `isTestRunnerId` onto `Metadata.runner`; absent in older and foreign zips, where the player shows no hint. Under WDIO a text carrying a `"` still emits XPath — WDIO compiles `tag*=` to XPath with `"` quoting and would build a broken expression — and WDIO resolves `//` itself. `locatorsMatch` decomposes **both** sides from either dialect, so `after.point` survives whichever way round the two grammars fall; the `concat()`-stitched literal is still left to exact comparison. `@wdio/elements`' standalone `getSnapshot` deliberately keeps the portable XPath default (its output is pasted into arbitrary tools), so a WDIO run's `browser.getSnapshot()` and its trace A11y tab disagree on that one branch.
- **A captured text locator is generated in the recording runner's dialect; every other branch is portable CSS.** `shared/locator-dialect.ts` `locatorDialect(runner)` is the one fact table — WDIO runners (`mocha`/`jasmine`/`cucumber`) get `a*=Logout`, `nightwatch*`/`selenium-webdriver` and an **unidentified recorder** get `//a[contains(., "Logout")]`. The id reaches the page script as `CaptureActionSnapshotInput.runner` and the zip as an extension field on `context-options`, read back through `isTestRunnerId` onto `Metadata.runner`; absent in older and foreign zips, where the player shows no hint. Under WDIO a text carrying a `"` still emits XPath — WDIO compiles `tag*=` to XPath with `"` quoting and would build a broken expression — and WDIO resolves `//` itself. `locatorsMatch` decomposes **both** sides from either dialect, so `after.point` survives whichever way round the two grammars fall; the `concat()`-stitched literal is still left to exact comparison. `@wdio/elements` `getSnapshot` on WebdriverIO ≥10.2 (`format: 'snapshot'`) is not on this table at all: it renders `@wdio/snapshot`'s `attachSelectors` choice, the renderer `wdio session snapshot --selectors` uses, which can be a WebdriverIO-v10-only strategy (`role/…`, `aria/…`), so a WDIO run's `browser.getSnapshot()` and its trace A11y tab can differ on that branch.
- `Metadata.runner` (typed `TestRunnerId`) and `metadata.options.framework` (untyped `string`, read by the sidebar's `getFramework`) are two carriers of the same fact. All three adapters now set both; the next change to either should collapse `getFramework` onto `Metadata.runner`.
- Selenium's `ctx.runner` (the *detected JS runner* — mocha/jest/cucumber, from `detectRunner()`) shadows the new `Metadata.runner` concept. Renamed to `detectedRunner` at the `driverMetadata` boundary only, where the two met inside one function and a swap would have been a silent dialect bug; the repo-wide rename in `plugin-internals.ts`/`session-lifecycle.ts` is still open.
- **A node:assert's target comes from VALUE PROVENANCE, not from the assert call.** node:assert takes values, so `assert.equal(await el.getText(), …)` names no element. `core/read-value-locators.ts` owns the value→locator registry (`rememberReadValue`/`selectorForReadValue`/`resolveAssertTargetFromArgs`; most-recent-producer-wins, 200 keys, ≤256 chars, primitives only), reached through `patchNodeAssert`'s optional `resolveAssertTarget` hook, which walks the **raw** args (sanitizing destroys handle identity) onto the shared `CommandLog.selector`. It lives in core because all three JS adapters feed it. **The registry is scoped to one test**: `clearReadValues` empties it when a test or scenario starts (the service's `resetStack` from `beforeTest`/`beforeScenario`, Selenium's `startTest`/`startScenario`, which a plain script reaches through the public `startTest`, and Nightwatch's `resetTestTracking`), so a later test's `assert.ok(true)` cannot box an element an earlier test read `true` from. Nightwatch's plugin hook fires once per module on the BDD interface, so `BrowserProxy.scopeReadValuesToRowTest` also clears when the running `it` (`rowTestUid`) changes, before every record and every lookup; a value a `beforeEach` hook reads is therefore dropped at the `it`'s first command or assertion, since Nightwatch still names the previous `it` while that hook runs. Python clears at `pytest_runtest_logstart`. **The WDIO service** feeds it from `SessionCapturer.afterCommand` for every row it logs: a command ACTION_MAP classes `Element` (`command-selectors.ts` `isElementCommand`) records the selector the row resolved, and every other read (`getTitle`, `getUrl`) records the null sentinel; `wireAssertCapture` passes `resolveAssertTargetFromArgs` to `patchNodeAssert`. A node:assert row with no known producer names nothing rather than the last element a locator resolved, which is what boxed `h2` for `assert.notEqual(await browser.getTitle(), …)`. The user's own `getTitle` is a `Page.getTitle` row: the service's snapshot probe reads the title from inside a hook whose stack holds the spec frame, so `action-snapshot.ts` runs its own title read inside an `AsyncLocalStorage` context and both hooks skip a `getTitle` observed inside it (`isServiceProbe`; WDIO awaits a command's hooks within the command, so they inherit its context). A count of probes in flight was not enough: core's probe timeout abandons a `getTitle` still running, and the count then hid a user `getTitle` issued in that window. Listing `getTitle` in `INTERNAL_COMMANDS` hid the user's call too. **Python** mirrors it in `selenium_devtools/read_value_locators.py`, since it cannot import core: every captured command records its result from the patched `execute` (a driver-level read records `None`; `get_attribute`/`is_displayed`, which reach the wire as scripts, resolve the element passed as their argument), and a pytest or plain-script assertion row looks up the observed operand before the expected one. In both languages the first operand with a known producer decides, so a subject recorded as owned by no element (a title or url read) ends the lookup instead of letting the expected value borrow the box of an element that once read the same text. **Selenium** feeds it from `driverPatcher`'s settle path and composes it with its handle WeakMap (`helpers/element-locators.ts`, adapter-local — it keys on `WebElement` identity, which no other framework has). **Nightwatch** feeds it from `browserProxy`'s capture callback via `assertTarget.commandTargetSelector`, an allowlist of element-read commands; every other command — every driver-level read (`title`, `getCurrentUrl`, `source`) included — records the **null sentinel**, without which a page-title assertion inherits the box of an element that reads the same text (measured on selenium's mocha example, `h1` vs `document.title` both "Example Domain"; reproduced and fixed for Nightwatch on a local `#hero`/`<title>` collision page). Measured: Nightwatch node:assert rows with a locator **0/7 → 4/7** (the 3 without are a title read, the title/`#hero` collision, and a literal); Selenium unchanged at 2/4 mocha and 2/3 cucumber. Residuals: boolean reads are weakly discriminating, so a value collision with no intervening producer can mis-attribute; Nightwatch's `element()/.find()` API and its ignored commands (`execute`, `executeAsync`, `perform`) are never wrapped, so their values neither claim nor clear. **`after.point` is deliberately not extended to assert rows** — `resolveActionPoint` gates on `class:'Element'` + `POINTABLE_METHODS`, so WDIO's folded assertion rows get none either; a point draws a click marker an assertion never earned.
Expand Down
14 changes: 7 additions & 7 deletions examples/wdio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"private": true,
"type": "module",
"devDependencies": {
"@wdio/cli": "10.0.2",
"@wdio/cucumber-framework": "10.0.1",
"@wdio/cli": "10.1.0",
"@wdio/cucumber-framework": "10.1.0",
"@wdio/devtools-service": "workspace:*",
"@wdio/globals": "10.0.2",
"@wdio/local-runner": "10.0.2",
"@wdio/mocha-framework": "10.0.1",
"@wdio/spec-reporter": "10.0.1",
"@wdio/types": "10.0.1",
"@wdio/globals": "10.1.0",
"@wdio/local-runner": "10.1.0",
"@wdio/mocha-framework": "10.1.0",
"@wdio/spec-reporter": "10.1.0",
"@wdio/types": "10.1.0",
"expect-webdriverio": "^8.0.0",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@typescript-eslint/parser": "^8.71.0",
"@typescript-eslint/utils": "^8.71.0",
"@vitest/coverage-v8": "^5.0.2",
"@wdio/allure-reporter": "^10.0.1",
"@wdio/allure-reporter": "^10.1.0",
"eslint": "^10.11.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
Expand All @@ -67,10 +67,10 @@
"unplugin-icons": "^24.0.0",
"vite": "^8.3.2",
"vitest": "^5.0.2",
"webdriverio": "^10.0.2",
"webdriverio": "^10.1.0",
"ws": "^8.22.0"
},
"dependencies": {
"@wdio/cli": "10.0.2"
"@wdio/cli": "10.1.0"
}
}
8 changes: 4 additions & 4 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@
"@iconify-json/mdi": "^1.2.3",
"@lit/context": "^1.1.6",
"@tailwindcss/postcss": "^4.3.3",
"@wdio/browser-runner": "^10.0.2",
"@wdio/browser-runner": "^10.1.0",
"@wdio/devtools-shared": "workspace:^",
"@wdio/devtools-trace": "workspace:^",
"@wdio/globals": "^10.0.2",
"@wdio/mocha-framework": "^10.0.1",
"@wdio/globals": "^10.1.0",
"@wdio/mocha-framework": "^10.1.0",
"@wdio/protocols": "10.0.2",
"@wdio/reporter": "10.0.1",
"@wdio/reporter": "10.1.0",
"codemirror": "^6.0.2",
"expect-webdriverio": "^8.0.0",
"lit": "^3.3.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@fastify/websocket": "^11.3.1",
"@wdio/devtools-app": "workspace:^",
"@wdio/devtools-script": "workspace:^",
"@wdio/logger": "10.0.0",
"@wdio/logger": "^10.1.0",
"fastify": "^5.12.5",
"fflate": "^0.8.2",
"get-port": "^7.2.0",
Expand All @@ -57,7 +57,7 @@
"devDependencies": {
"@types/shell-quote": "^1.7.5",
"@types/ws": "^8.18.2",
"@wdio/cli": "^10.0.2",
"@wdio/cli": "^10.1.0",
"@wdio/devtools-shared": "workspace:^",
"@wdio/devtools-trace": "workspace:^",
"nodemon": "^3.1.14",
Expand Down
12 changes: 6 additions & 6 deletions packages/conformance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@
"@types/selenium-webdriver": "^4.35.6",
"@types/ws": "^8.18.2",
"@types/yazl": "^3.3.1",
"@wdio/cli": "10.0.2",
"@wdio/cli": "10.1.0",
"@wdio/devtools-service": "workspace:^",
"@wdio/devtools-shared": "workspace:^",
"@wdio/devtools-trace": "workspace:^",
"@wdio/globals": "10.0.2",
"@wdio/local-runner": "10.0.2",
"@wdio/mocha-framework": "10.0.1",
"@wdio/globals": "10.1.0",
"@wdio/local-runner": "10.1.0",
"@wdio/mocha-framework": "10.1.0",
"@wdio/nightwatch-devtools": "workspace:^",
"@wdio/selenium-devtools": "workspace:^",
"@wdio/spec-reporter": "10.0.1",
"@wdio/spec-reporter": "10.1.0",
"expect-webdriverio": "^8.0.0",
"fflate": "^0.8.2",
"mocha": "^12.0.3",
"nightwatch": "^3.16.0",
"selenium-webdriver": "^4.50.0",
"vite": "^8.3.2",
"webdriverio": "^10.0.2",
"webdriverio": "^10.1.0",
"ws": "^8.22.0",
"yazl": "^3.3.1"
}
Expand Down
7 changes: 7 additions & 0 deletions packages/conformance/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ export const RENDERED_AFTER_MS: Record<string, number> = { '#flash': 150 }

export const BROKEN_PATH = '/broken'

/** W3C's session defaults: a real browser always answers `GET /timeouts` with all three, and WebdriverIO 10.1 reads `script` from it before every BiDi `execute`. */
export const DEFAULT_TIMEOUTS = {
script: 30_000,
pageLoad: 300_000,
implicit: 0
}

export const BIDI_PATH = '/session/conformance/bidi'

/** BiDi acknowledgements whose callers read a field back; commands the fake browser models are answered from its state. */
Expand Down
25 changes: 12 additions & 13 deletions packages/conformance/src/fake-webdriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
BLANK_PNG,
BROKEN_PATH,
COLLECTOR_SIGNATURE,
DEFAULT_TIMEOUTS,
DOCUMENT_MIME,
ELEMENT_KEY,
ELEMENT_TEXT,
Expand Down Expand Up @@ -62,6 +63,13 @@ type Reply = {

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))

const FIXED_REPLIES: Record<string, unknown> = {
'/screenshot': BLANK_PNG,
'/window/rect': { x: 0, y: 0, width: 1280, height: 720 },
'/window': WINDOW_HANDLE,
'/window/handles': [WINDOW_HANDLE]
}

/** A W3C WebDriver over a two-page model with one late-rendered element; without a preload its collector anchors only when a drain forces it. */
export class FakeWebDriver {
readonly origin: string
Expand Down Expand Up @@ -217,26 +225,17 @@ export class FakeWebDriver {
if (method === 'POST' && (path === '/element' || path === '/elements')) {
return this.#find(String(params.value), path === '/elements')
}
if (method === 'GET' && path === '/timeouts') {
return { value: DEFAULT_TIMEOUTS }
}
const element = path.match(/^\/element\/([^/]+)\/(value|click|clear|text)$/)
if (element) {
return this.#onElement(element[1], element[2], params)
}
if (method === 'POST' && path.startsWith('/execute/')) {
return this.#executeReply(params)
}
if (path === '/screenshot') {
return { value: BLANK_PNG }
}
if (path === '/window/rect') {
return { value: { x: 0, y: 0, width: 1280, height: 720 } }
}
if (path === '/window') {
return { value: WINDOW_HANDLE }
}
if (path === '/window/handles') {
return { value: [WINDOW_HANDLE] }
}
return { value: null }
return { value: FIXED_REPLIES[path] ?? null }
}

async #navigate(path: string): Promise<Reply> {
Expand Down
Loading
Loading