Skip to content

[js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi#17574

Merged
AutomatedTester merged 11 commits into
SeleniumHQ:trunkfrom
pujagani:js-bidi-codegen
Jun 8, 2026
Merged

[js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi#17574
AutomatedTester merged 11 commits into
SeleniumHQ:trunkfrom
pujagani:js-bidi-codegen

Conversation

@pujagani
Copy link
Copy Markdown
Contributor

@pujagani pujagani commented May 26, 2026

🔗 Related Issues

Related to #17487
Part of the CDDL WebDriver BiDi API generator. Implemented as defined in the spec https://gist.github.com/AutomatedTester/148437f846a3759f7678c7ff6debb210

💥 What does this PR do?

Adds a code generator (generate_bidi.mjs) that reads the merged WebDriver BiDi CDDL
specification and emits one typed TypeScript module per domain into bidi/generated/.
The generator is wired into the Bazel build so the output is compiled and included in
the npm package automatically.

Each generated domain class (Browser, BrowsingContext, Input, Log, Network,
Script, Storage, etc.) exposes typed command methods and event subscription helpers
via a static async create(driver) factory. An enhancements manifest
(private/bidi_enhancements_manifest.json) allows hand-written additions (deprecated
compatibility shims, convenience wrappers, and method overloads) to be injected at
build time without modifying generated files.

🔧 Implementation Notes

Why a two-pass generator?
cddl2ts only produces type declarations; it has no concept of the BiDi command/event
distinction. A second pass walks the raw CDDL AST to identify commands (groups with a
method literal and params) and events (same structure plus type: "event") and
emits the implementation class from that.

Why throw on error responses?
bidi/index.js always resolves, it never rejects on BiDi error responses. Both void
and non-void generated methods inspect the response payload and throw if
response.type === 'error', so unhandled server errors surface as test failures rather
than being silently swallowed.

Why an enhancements manifest?
The existing hand-crafted modules use a builder pattern
(getBrowserInstance(driver), AddInterceptParameters). The generated API uses typed
params objects. Replacing the old exports directly would be a breaking change. The
manifest lets deprecated compatibility wrappers live alongside generated code until a
major version bump removes them.

🤖 AI assistance

  • AI assisted (complete below)
    • Tool(s): Claude (Anthropic)
    • What was generated: generator implementation, Bazel macro, enhancements manifest,
      integration tests under test/bidi/generated/
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

Breaking change: The generated API shape (Domain.create(driver), typed params
objects) differs from the existing hand-crafted modules (positional args, builder
classes). Deprecated compatibility shims are in place for the most-used modules but a
full audit is needed before the old modules can be removed. See the Phase 7 audit in
the PR description or tracking issue for the complete list.

Follow-on work required:

  • Add missing convenience wrappers to the enhancements manifest:
    captureBoxScreenshot, captureElementScreenshot, locateNode, locateElement,
    locateElements (BrowsingContext); filtered log level variants (Log).
  • Decide if deprecation is required for browsingContextInspector.js and networkInspector.js — superseded by
    event methods on the generated BrowsingContext and Network classes
  • Decide fate of the builder/DTO classes (AddInterceptParameters,
    ContinueRequestParameters, CookieFilter, etc.).

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests)

@selenium-ci selenium-ci added C-py Python Bindings C-nodejs JavaScript Bindings B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related labels May 26, 2026
@qodo-code-review
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add JavaScript/TypeScript CDDL code generator for WebDriver BiDi with comprehensive test coverage

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Implements a comprehensive WebDriver BiDi code generator (generate_bidi.mjs) that transforms
  CDDL specifications into typed TypeScript domain classes
• Two-pass generation approach: Pass 1 uses cddl2ts for type declarations with post-processing;
  Pass 2 walks raw CDDL AST to extract commands and events for implementation
• Generates per-domain modules (Browser, BrowsingContext, Input, Log, Network, Script,
  Storage, Emulation) with typed command methods and event subscription helpers
• Introduces enhancements manifest (bidi_enhancements_manifest.json) for injecting hand-written
  additions (deprecated compatibility shims, convenience wrappers) without modifying generated code
• Integrates code generation into Bazel build pipeline via generate_bidi_library() macro that
  orchestrates CDDL merging, TypeScript generation, and compilation
• Comprehensive integration test suite covering all generated BiDi domains with Chrome and Firefox
  browser validation
• Adds cddl@^0.20.1 and cddl2ts@^0.9.1 dev dependencies and updates pnpm workspace configuration
  for dependency resolution
• Expands visibility of CDDL merge tool to JavaScript build pipeline
Diagram
flowchart LR
  CDDL["CDDL Specifications<br/>WebDriver BiDi, Permissions,<br/>Prefetch, UA Client Hints"]
  Merge["Merge CDDL<br/>merge_cddl"]
  Gen["Two-Pass Generator<br/>generate_bidi.mjs"]
  Types["Type Declarations<br/>cddl2ts Pass 1"]
  Impl["Implementation Classes<br/>Pass 2 AST Walk"]
  Manifest["Enhancements Manifest<br/>bidi_enhancements_manifest.json"]
  Output["Generated Domain Modules<br/>Browser, BrowsingContext,<br/>Input, Log, Network,<br/>Script, Storage, Emulation"]
  Tests["Integration Tests<br/>All domains + browsers"]
  Package["npm Package"]
  
  CDDL --> Merge
  Merge --> Gen
  Gen --> Types
  Gen --> Impl
  Manifest --> Gen
  Types --> Output
  Impl --> Output
  Output --> Tests
  Output --> Package
  Tests -.-> Package

Loading

Grey Divider

File Changes

1. javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js 🧪 Tests +324/-0

Integration tests for generated BrowsingContext BiDi domain

• Comprehensive integration tests for the generated BrowsingContext domain class
• Tests cover context creation/closure, navigation, viewport/screenshot, user prompts, and event
 listeners
• Validates typed command methods (create(), navigate(), reload()) and event subscriptions
 (onContextCreated(), onNavigationCommitted())
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js


2. javascript/selenium-webdriver/test/bidi/generated/script_test.js 🧪 Tests +314/-0

Integration tests for generated Script BiDi domain

• Integration tests for the generated Script domain class covering script evaluation and execution
• Tests evaluate(), callFunction(), getRealms(), and preload script management
• Validates realm creation/destruction events and exception handling
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/script_test.js


3. javascript/selenium-webdriver/test/bidi/generated/emulation_test.js 🧪 Tests +297/-0

Integration tests for generated Emulation BiDi domain

• Integration tests for the generated Emulation domain class
• Tests user agent override, timezone/locale override, network conditions, geolocation, screen
 orientation, and scripting/touch emulation
• Validates context-specific and global emulation settings
• Tests both Chrome and Firefox browsers with browser-specific test skipping

javascript/selenium-webdriver/test/bidi/generated/emulation_test.js


View more (15)
4. javascript/selenium-webdriver/test/bidi/generated/network_test.js 🧪 Tests +268/-0

Integration tests for generated Network BiDi domain

• Integration tests for the generated Network domain class
• Tests request/response events, interception, cache behavior, and extra headers
• Validates onBeforeRequestSent(), onResponseCompleted(), addIntercept(), and
 continueRequest() methods
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/network_test.js


5. javascript/selenium-webdriver/test/bidi/generated/storage_test.js 🧪 Tests +229/-0

Integration tests for generated Storage BiDi domain

• Integration tests for the generated Storage domain class
• Tests cookie management: setCookie(), getCookies(), deleteCookies() with various filters
• Validates partition key handling and domain/context-specific cookie operations
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/storage_test.js


6. javascript/selenium-webdriver/test/bidi/generated/input_test.js 🧪 Tests +203/-0

Integration tests for generated Input BiDi domain

• Integration tests for the generated Input domain class
• Tests keyboard and pointer actions via performActions(), and releaseActions()
• Validates file dialog event handling with browser-specific test skipping
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/input_test.js


7. javascript/selenium-webdriver/test/bidi/generated/log_test.js 🧪 Tests +193/-0

Integration tests for generated Log BiDi domain

• Integration tests for the generated Log domain class
• Tests console entry listening, JavaScript exception capture, and log entry filtering
• Validates onConsoleEntry(), onJavascriptException(), and onEntryAdded() event methods
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/log_test.js


8. javascript/selenium-webdriver/test/bidi/generated/browser_test.js 🧪 Tests +121/-0

Integration tests for generated Browser BiDi domain

• Integration tests for the generated Browser domain class
• Tests user context creation/removal and client window information retrieval
• Validates createUserContext(), getUserContexts(), removeUserContext(), and
 getClientWindows() methods
• Tests both Chrome and Firefox browsers

javascript/selenium-webdriver/test/bidi/generated/browser_test.js


9. javascript/selenium-webdriver/private/generate_bidi.bzl ⚙️ Configuration changes +154/-0

Bazel build rules for WebDriver BiDi code generation

• Bazel macro generate_bidi_library() that orchestrates CDDL merging, TypeScript generation, and
 compilation
• Implements three-step build process: merge CDDL files, run generate_bidi.mjs generator, compile
 TypeScript to JavaScript
• Custom _compile_bidi_ts rule handles TypeScript compilation with proper output directory
 handling
• Supports enhancements manifest for per-domain customizations

javascript/selenium-webdriver/private/generate_bidi.bzl


10. pnpm-lock.yaml Dependencies +122/-15

Lock file updates for CDDL code generation tools

• Adds cddl@0.20.1 and cddl2ts@0.9.1 as dev dependencies for CDDL parsing and TypeScript
 generation
• Updates package extension checksums and adds new transitive dependencies (ast-types, recast,
 yargs, etc.)
• Removes obsolete ts-declaration-location dependency

pnpm-lock.yaml


11. javascript/selenium-webdriver/private/bidi_enhancements_manifest.json ⚙️ Configuration changes +123/-0

Enhancements manifest for generated BiDi domains

• JSON manifest defining per-domain enhancements for generated BiDi modules
• Specifies deprecated compatibility wrappers (e.g., getBrowserInstance(), getNetworkInstance())
• Defines convenience method overloads for Network, Script, BrowsingContext, and Input
 domains
• Allows hand-written additions without modifying generated code

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json


12. javascript/selenium-webdriver/BUILD.bazel ⚙️ Configuration changes +30/-1

Bazel build integration for BiDi code generation

• Adds js_binary import for the generator script
• Defines generate_bidi_script target wrapping generate_bidi.mjs with CDDL and cddl2ts
 dependencies
• Calls generate_bidi_library() macro to generate BiDi modules from merged CDDL specs (WebDriver
 BiDi, Permissions, Prefetch, UA Client Hints, Web Bluetooth)
• Includes generated BiDi source in the npm package

javascript/selenium-webdriver/BUILD.bazel


13. javascript/selenium-webdriver/package.json Dependencies +2/-0

Dev dependencies for CDDL code generation

• Adds cddl@^0.20.1 and cddl2ts@^0.9.1 as dev dependencies for CDDL parsing and TypeScript code
 generation

javascript/selenium-webdriver/package.json


14. pnpm-workspace.yaml ⚙️ Configuration changes +20/-0

pnpm workspace configuration for dependency resolution

• Adds onlyBuiltDependencies configuration
• Defines packageExtensions for transitive dependency resolution (google-closure-deps,
 minimatch, brace-expansion, recast)
• Ensures forward compatibility with pnpm v11+ by using workspace-level configuration

pnpm-workspace.yaml


15. javascript/selenium-webdriver/private/BUILD.bazel ⚙️ Configuration changes +11/-0

Bazel build file for private BiDi enhancements

• New Bazel build file for the private directory
• Copies bidi_enhancements_manifest.json to bin directory for build-time access
• Exports the manifest file for use by the code generator

javascript/selenium-webdriver/private/BUILD.bazel


16. py/private/BUILD.bazel ⚙️ Configuration changes +4/-1

Visibility update for CDDL merge tool

• Expands visibility of merge_cddl Python binary to include
 //javascript/selenium-webdriver:__pkg__
• Allows the JavaScript BiDi code generation pipeline to use the CDDL merging tool

py/private/BUILD.bazel


17. javascript/selenium-webdriver/generate_bidi.mjs ✨ Enhancement +929/-0

WebDriver BiDi TypeScript code generator from CDDL specification

• Implements a two-pass code generator that transforms WebDriver BiDi CDDL specifications into typed
 TypeScript domain classes
• Pass 1 uses cddl2ts to generate type declarations, then post-processes to deduplicate exports
 and replace any with unknown
• Pass 2 walks the raw CDDL AST to extract commands (groups with method literal and params) and
 events, generating implementation classes with typed methods
• Generates per-domain files with cross-domain import resolution, error handling for BiDi responses,
 and support for enhancements manifest for compatibility shims

javascript/selenium-webdriver/generate_bidi.mjs


18. package.json Additional files +0/-20

...

package.json


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented May 26, 2026

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (7)

Grey Divider


Action required

1. Event name can crash ✓ Resolved 🐞 Bug ☼ Reliability
Description
bidi/index.js now emits protocol events using payload.method as the EventEmitter event name; if a
peer sends method === 'error' and there is no 'error' listener, Node will throw and crash the
process. This makes the BiDi transport vulnerable to process-level termination from a single
unexpected/malformed incoming frame.
Code

javascript/selenium-webdriver/bidi/index.js[R88-90]

+        if (payload != null && typeof payload.method === 'string') {
+          this.emit(payload.method, payload.params)
+        }
Evidence
The dispatcher emits events using payload.method directly; this can include the reserved 'error'
event name, and the same module already treats emitting 'error' as dangerous by guarding it when
wrapping JSON parse failures.

javascript/selenium-webdriver/bidi/index.js[18-35]
javascript/selenium-webdriver/bidi/index.js[72-79]
javascript/selenium-webdriver/bidi/index.js[82-91]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Index` emits BiDi events with `this.emit(payload.method, payload.params)`. In Node, `'error'` is a special EventEmitter event: emitting it without listeners throws, which can crash the process. Since `payload.method` comes from the remote peer, a malformed/buggy peer can cause a hard crash.

### Issue Context
The file already avoids emitting `'error'` for JSON parse failures unless an error listener exists, indicating we need similar defensive handling for protocol events.

### Fix Focus Areas
- javascript/selenium-webdriver/bidi/index.js[82-91]

### Suggested fix
Special-case reserved event names (at minimum `'error'`) before emitting:
- If `payload.method === 'error'`:
 - either emit a safe alternative event name (e.g. `'bidi.error'`) and/or
 - route it to the existing warning/error-handling path (`listenerCount('error')` guard + `process.emitWarning`).

Keep emitting the normal method name for all other events so generated domain classes remain compatible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Empty context in back/forward 🐞 Bug ≡ Correctness
Description
The BrowsingContext enhancement methods back() and forward() always call traverseHistory with
context: '' (empty string), so these convenience methods will consistently fail with an invalid
browsing context id.
Code

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[R12-13]

+      "back": "  async back(): Promise<void> {\n    await this.traverseHistory({ context: '', delta: -1 } as BrowsingContextTraverseHistoryParameters)\n  }",
+      "forward": "  async forward(): Promise<void> {\n    await this.traverseHistory({ context: '', delta: 1 } as BrowsingContextTraverseHistoryParameters)\n  }",
Evidence
The manifest hard-codes an empty string for context, while the generated API (and its tests) use a
real contextId for traverseHistory, demonstrating the required value cannot be empty.

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[8-16]
javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js[98-113]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`back()`/`forward()` in the enhancements manifest hard-code `context: ''` and therefore cannot work against any BiDi server.

### Issue Context
The generated API is params-object based; these helpers must either accept a context id or be removed until a proper abstraction exists.

### Fix Focus Areas
- javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[8-16]

### Suggested fix
- Change the shim signatures to accept a context id:
 - `async back(context: BrowsingContextBrowsingContext): Promise<void>`
 - `async forward(context: BrowsingContextBrowsingContext): Promise<void>`
- Delegate to `traverseHistory({ context, delta: ±1 })` (no empty string).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. continueWithAuth ignores errors 🐞 Bug ≡ Correctness
Description
The Network enhancement overrides continueWithAuth() and calls this.bidi.send() directly without
checking for BiDi error responses, so server-side errors are silently ignored instead of thrown like
other generated commands.
Code

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[R49-51]

+      "cancelAuth": "  async cancelAuth(requestId: NetworkRequest): Promise<void> {\n    await this.continueWithAuth({ request: requestId, action: 'cancel' } as NetworkContinueWithAuthParameters)\n  }",
+      "continueWithAuthNoCredentials": "  async continueWithAuthNoCredentials(requestId: NetworkRequest): Promise<void> {\n    await this.continueWithAuth({ request: requestId, action: 'default' } as NetworkContinueWithAuthParameters)\n  }",
+      "continueWithAuth": "  async continueWithAuth(params: NetworkContinueWithAuthParameters): Promise<void>\n  /** @deprecated Pass a NetworkContinueWithAuthParameters object instead — will be removed in a future major version. */\n  async continueWithAuth(requestId: NetworkRequest, username: string, password: string): Promise<void>\n  async continueWithAuth(\n    paramsOrRequestId: NetworkContinueWithAuthParameters | NetworkRequest,\n    username?: string,\n    password?: string,\n  ): Promise<void> {\n    const params: NetworkContinueWithAuthParameters =\n      typeof paramsOrRequestId === 'string'\n        ? ({\n            request: paramsOrRequestId,\n            action: 'provideCredentials',\n            credentials: { type: 'password', username: username!, password: password! },\n          } as NetworkContinueWithAuthParameters)\n        : (paramsOrRequestId as NetworkContinueWithAuthParameters)\n    await this.bidi.send({\n      method: 'network.continueWithAuth',\n      params: params as unknown as Record<string, unknown>,\n    })\n  }",
Evidence
The enhancements manifest’s override sends the command but does not inspect the returned payload for
error responses. The generator explicitly emits an error-check for all commands, and the underlying
transport resolves with raw payloads, so skipping the check makes errors silent.

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[45-57]
javascript/selenium-webdriver/generate_bidi.mjs[860-888]
javascript/selenium-webdriver/bidi/index.js[177-208]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `continueWithAuth` enhancement override bypasses the generator’s standard `response.type === 'error'` check. Since `bidi.send()` resolves with the raw payload, this makes failures silent.

### Issue Context
`generate_bidi.mjs` intentionally makes generated commands throw on `response['type'] === 'error'`. Overriding a command should preserve this behavior.

### Fix Focus Areas
- javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[45-57]
- javascript/selenium-webdriver/generate_bidi.mjs[860-891]
- javascript/selenium-webdriver/bidi/index.js[177-208]

### Suggested fix
- In the override, capture the response from `this.bidi.send(...)` and apply the same error check pattern as generated methods.
 - If `response.type === 'error'`, throw an `Error` with the server-provided details.
- Alternatively, avoid overriding the generated `continueWithAuth(params)` method:
 - Keep the generated method intact.
 - Add a separate deprecated wrapper name for the legacy positional signature that calls the generated method with a constructed params object.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. username! in continueWithAuth 📘 Rule violation ≡ Correctness
Description
The injected deprecated overload for Network.continueWithAuth uses non-null assertions
(username!, password!) on optional parameters, so invalid calls can produce undefined
credentials or unclear runtime failures. Protocol-derived/user inputs should be validated and
rejected with deterministic, actionable errors.
Code

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[51]

+      "continueWithAuth": "  async continueWithAuth(params: NetworkContinueWithAuthParameters): Promise<void>\n  /** @deprecated Pass a NetworkContinueWithAuthParameters object instead — will be removed in a future major version. */\n  async continueWithAuth(requestId: NetworkRequest, username: string, password: string): Promise<void>\n  async continueWithAuth(\n    paramsOrRequestId: NetworkContinueWithAuthParameters | NetworkRequest,\n    username?: string,\n    password?: string,\n  ): Promise<void> {\n    const params: NetworkContinueWithAuthParameters =\n      typeof paramsOrRequestId === 'string'\n        ? ({\n            request: paramsOrRequestId,\n            action: 'provideCredentials',\n            credentials: { type: 'password', username: username!, password: password! },\n          } as NetworkContinueWithAuthParameters)\n        : (paramsOrRequestId as NetworkContinueWithAuthParameters)\n    await this.bidi.send({\n      method: 'network.continueWithAuth',\n      params: params as unknown as Record<string, unknown>,\n    })\n  }",
Evidence
PR Compliance ID 11 requires early validation of external/protocol/user-derived inputs and
discourages null-forgiving operators like !. The new injected method body constructs credentials
using username! and password! without checking they are present.

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[51-51]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The deprecated overload implementation for `Network.continueWithAuth` uses `username!`/`password!` non-null assertions even though the implementation signature allows them to be omitted, which can lead to undefined credentials being sent or confusing runtime errors.

## Issue Context
This code is injected from the enhancements manifest and becomes part of the published/generated JS/TS API surface.

## Fix Focus Areas
- javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[51-51]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Misleading null-manifest error 🐞 Bug ⚙ Maintainability ⭐ New
Description
loadEnhancements() rejects a manifest whose parsed JSON is null, but the thrown error message
reports the type as "object" (because typeof null === 'object'), making build failures harder to
diagnose. This only affects developer-facing diagnostics during codegen/build failures.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R260-262]

+    throw new Error(
+      `Enhancements manifest at ${fullPath} must be a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}`,
+    )
Evidence
The code explicitly treats parsed === null as invalid, but the error message only distinguishes
arrays vs typeof parsed, which evaluates to 'object' for null and therefore misreports the
actual input type.

javascript/selenium-webdriver/generate_bidi.mjs[246-265]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`loadEnhancements()` throws an error when the parsed manifest is not a JSON object. However, if the manifest is `null`, the error message currently says it "got object" due to JavaScript’s `typeof null === 'object'` behavior.

## Issue Context
This affects build/codegen diagnostics only, but it’s an easy improvement that reduces confusion when contributors accidentally set the manifest to `null`.

## Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[246-265]

## Suggested change
Adjust the error message construction to explicitly detect `null`:
- If `parsed === null`, print `null`
- Else if `Array.isArray(parsed)`, print `array`
- Else print `typeof parsed`

This keeps the existing validation logic but produces accurate diagnostics.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Manifest schema unchecked 🐞 Bug ☼ Reliability
Description
loadEnhancements() only validates the manifest is a JSON object, but generation later assumes
per-domain fields like excludeMethods/excludeTypes are arrays and calls .includes() on them. A
malformed manifest can therefore crash codegen with a TypeError instead of a clear validation error,
breaking Bazel builds non-deterministically for contributors editing the manifest.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R253-262]

+  let parsed
+  try {
+    parsed = JSON.parse(readFileSync(fullPath, 'utf8'))
+  } catch (err) {
+    throw new Error(`Failed to parse enhancements manifest at ${fullPath}: ${err.message}`)
+  }
+  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
+    throw new Error(`Enhancements manifest at ${fullPath} must be a JSON object, got ${Array.isArray(parsed) ? 'array' : typeof parsed}`)
+  }
+  return parsed
Evidence
The loader returns the parsed object after only a top-level object check, while generation later
calls .includes() on enhancement.excludeMethods and excludeTypes without verifying they are
arrays; non-array values will throw at runtime during generation.

javascript/selenium-webdriver/generate_bidi.mjs[246-263]
javascript/selenium-webdriver/generate_bidi.mjs[744-752]
javascript/selenium-webdriver/generate_bidi.mjs[809-818]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`loadEnhancements()` currently validates only that the parsed manifest is a non-null object. Later code assumes each domain entry has the expected shape (arrays for `excludeMethods`/`excludeTypes`, object for `extraMethods`, string for `extraTypes`) and will throw a `TypeError` (e.g., calling `.includes` on a non-array) if the manifest is malformed.

### Issue Context
This failure would happen during code generation and show up as an opaque runtime exception, rather than a targeted error message pointing to the invalid domain key/property.

### Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[253-262]

### Suggested fix
1. After parsing, iterate `Object.entries(parsed)` and validate each domain config:
  - domain config must be a plain object (non-null, not array)
  - `excludeMethods`/`excludeTypes` must be arrays when present
  - `extraMethods` must be a plain object when present
  - `extraTypes` must be a string when present
2. Throw an error that names the domain and field (e.g., `"Enhancements manifest: domain 'network' excludeMethods must be an array"`).
3. (Optional) Normalize missing fields to defaults (`[]`, `{}`, `""`) so the rest of the generator can rely on consistent shapes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. loadEnhancements() lacks JSON validation ✓ Resolved 📘 Rule violation ☼ Reliability
Description
loadEnhancements() parses the enhancements manifest with JSON.parse() without validating the
JSON or providing a clear, contextual error message on parse failure. A malformed/partial manifest
can therefore fail generation with a generic exception rather than a deterministic, actionable
failure in CI/build automation.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R237-245]

+function loadEnhancements(manifestPath) {
+  if (!manifestPath) return {}
+  const fullPath = resolveInputPath(manifestPath)
+  if (!existsSync(fullPath)) {
+    console.warn(`Warning: enhancements manifest not found: ${fullPath}`)
+    return {}
+  }
+  return JSON.parse(readFileSync(fullPath, 'utf8'))
+}
Evidence
PR Compliance ID 12 requires automation scripts to validate external outputs/inputs and fail safely
with explicit errors. The new generator reads an external JSON manifest and calls JSON.parse(...)
directly, so invalid JSON will throw without a clear message pointing to the manifest path or
expected structure.

javascript/selenium-webdriver/generate_bidi.mjs[237-245]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`loadEnhancements()` uses `JSON.parse()` on the enhancements manifest without guarding for invalid JSON or validating the parsed shape. This can cause confusing failures in Bazel/CI when the manifest is malformed.

## Issue Context
This generator is run in build automation and should fail fast with clear diagnostics that identify the problematic file and reason.

## Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[237-245]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (10)
8. Windows path strip bug ✓ Resolved 🐞 Bug ☼ Reliability
Description
resolveInputPath() strips the BAZEL_BINDIR prefix using a hard-coded '/' separator, which can fail
on Windows and yield incorrectly resolved input paths (often double-prefixed) inside Bazel actions.
This can make the generator fail to locate the --cddl/--enhancements inputs on Windows builds.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R138-143]

+function resolveInputPath(p) {
+  if (!p) return null
+  if (!process.env.BAZEL_BINDIR) return resolve(p)
+  const prefix = process.env.BAZEL_BINDIR + '/'
+  return resolve(p.startsWith(prefix) ? p.slice(prefix.length) : p)
+}
Evidence
The generator’s prefix stripping uses a literal BAZEL_BINDIR + '/' which is not Windows-safe,
while other Bazel tooling in this repo explicitly handles Windows path differences (indicating
Windows support is expected).

javascript/selenium-webdriver/generate_bidi.mjs[128-143]
javascript/private/jsdoc.bzl[11-24]
javascript/private/closure_js_deps.bzl[49-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`resolveInputPath()` assumes forward slashes when stripping the `BAZEL_BINDIR` prefix. On Windows, Bazel/tools often produce paths using backslashes, causing the prefix check to fail and resulting in wrong path resolution.

### Issue Context
The generator is designed to run under Bazel where `js_binary` may `chdir()` into `BAZEL_BINDIR`. Input paths expanded via `$(location ...)` need to be made relative to that CWD.

### Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[138-143]

### Suggested fix
Update `resolveInputPath` to normalize both `p` and `process.env.BAZEL_BINDIR` before prefix stripping, e.g.:
- Convert both strings to a single separator style (`replaceAll('\\', '/')`) and strip using that.
- Or use `path.normalize()` and compare using normalized strings.
- Consider using `path.posix` consistently for Bazel-generated paths if they’re always `/`-separated, and explicitly normalize `BAZEL_BINDIR`/args to posix before matching.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Missing wait for event ✓ Resolved 🐞 Bug ☼ Reliability
Description
The generated browsingContext navigationCommitted test asserts navEvent immediately after
navigate() completes without waiting for the WebSocket event listener to run, making the test
timing-dependent and potentially flaky. Other event tests in the same file correctly use
driver.wait(() => event !== null, 5000) before asserting.
Code

javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js[R215-229]

+      it('fires navigationCommitted on page navigate', async function () {
+        const contextId = await driver.getWindowHandle()
+        let navEvent = null
+
+        await browsingContext.onNavigationCommitted((params) => {
+          if (params.context === contextId) {
+            navEvent = params
+          }
+        })
+
+        await browsingContext.navigate({ context: contextId, url: Pages.emptyPage, wait: 'complete' })
+
+        assert.ok(navEvent, 'navigationCommitted event should have fired')
+        assert.strictEqual(navEvent.context, contextId)
+        assert.ok(navEvent.url)
Evidence
The test currently asserts immediately, while nearby event tests use explicit waits, demonstrating
the risk of event delivery racing with the assertion.

javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js[180-230]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`navigationCommitted` test asserts immediately after navigation without waiting for the event callback to fire.

### Issue Context
The same file uses `driver.wait(() => event !== null, 5000)` for other event assertions (e.g., contextCreated/contextDestroyed, fragmentNavigated), so this test is inconsistent and can be racy.

### Fix Focus Areas
- javascript/selenium-webdriver/test/bidi/generated/browsing_context_test.js[214-230]

### Suggested fix
Insert:
```js
await driver.wait(() => navEvent !== null, 5000)
```
between the `navigate(...)` call and the assertions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. BiDi generator lacks unit tests 📘 Rule violation ➹ Performance
Description
The PR validates the generated BiDi bindings primarily via browser-based tests that require real
Chrome/Firefox sessions, which are slower and more failure-prone than small/unit tests. Where
feasible, generator correctness (type splitting, AST command/event extraction, manifest injection)
should be covered by small tests to improve speed and reliability.
Code

javascript/selenium-webdriver/test/bidi/generated/browser_test.js[R29-35]

+suite(
+  function (env) {
+    let driver
+
+    beforeEach(async function () {
+      driver = await env.builder().build()
+    })
Evidence
PR Compliance ID 4 prefers small/unit tests where feasible. The added tests instantiate real
browsers via env.builder().build() and explicitly run across Browser.CHROME and
Browser.FIREFOX, indicating the new coverage is primarily end-to-end rather than unit-level for
generator logic.

AGENTS.md: Prefer Unit (Small) Tests and Avoid Mocks that Misrepresent API Contracts
javascript/selenium-webdriver/test/bidi/generated/browser_test.js[29-35]
javascript/selenium-webdriver/test/bidi/generated/browser_test.js[119-121]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new BiDi code generator functionality is exercised via end-to-end browser tests, but lacks small/unit tests that validate the generator logic deterministically.

## Issue Context
Rule prefers small/unit tests where feasible to reduce flake and runtime. Much of the generator behavior (parsing fixtures, command/event extraction, type post-processing, enhancements injection) can be verified without starting browsers.

## Fix Focus Areas
- javascript/selenium-webdriver/test/bidi/generated/browser_test.js[29-35]
- javascript/selenium-webdriver/test/bidi/generated/browser_test.js[119-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Missing BiDi support check 🐞 Bug ☼ Reliability
Description
Generated Domain.create(driver) calls driver.getBidi() without verifying the session supports
BiDi, so unsupported sessions can throw confusing TypeErrors (e.g., dereferencing webSocketUrl).
Existing hand-written BiDi entrypoints validate webSocketUrl first and throw a clear "WebDriver
instance must support BiDi protocol" error.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R828-831]

+  lines.push(`  static async create(driver: unknown): Promise<${className}> {`)
+  lines.push(`    const bidi = await (driver as { getBidi(): Promise<BidiConnection> }).getBidi()`)
+  lines.push(`    return new ${className}(bidi)`)
+  lines.push(`  }`)
Evidence
The generated factory unconditionally calls getBidi(), while WebDriver#getBidi() assumes
webSocketUrl exists and immediately calls .replace(...) on it; existing BiDi modules explicitly
check capabilities before calling getBidi().

javascript/selenium-webdriver/generate_bidi.mjs[822-832]
javascript/selenium-webdriver/lib/webdriver.js[1308-1313]
javascript/selenium-webdriver/bidi/browser.js[29-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Generated domain factories (`static async create(driver)`) call `driver.getBidi()` without validating BiDi support. When `webSocketUrl` is absent, `WebDriver#getBidi()` will fail with a non-actionable runtime error.

### Issue Context
The existing hand-written BiDi modules call `driver.getCapabilities().get('webSocketUrl')` and throw a clear error before attempting a BiDi connection; generated domains should match this behavior for consistency and debuggability.

### Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[822-832]
- javascript/selenium-webdriver/lib/webdriver.js[1308-1313]
- javascript/selenium-webdriver/bidi/browser.js[29-35]

### Proposed fix
In `generateClass`, emit a guard inside `static async create(driver)`:
- Check `driver` has `getCapabilities()` and that `caps.get('webSocketUrl')` is truthy.
- If not, throw `Error('WebDriver instance must support BiDi protocol')` (matching existing modules).
- Then call `getBidi()` as today.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. parse(cddlPath) lacks precheck 📘 Rule violation ☼ Reliability
Description
The generator parses the --cddl input without first validating that the resolved path exists/is
readable, which can lead to unclear failures in CI/build actions. This violates the requirement to
validate preconditions for automation scripts and fail safely with explicit errors.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R171-179]

+  const cddlPath = resolveInputPath(args.cddl)
+  const outputDir = resolve(args['output-dir'])
+  const specVersion = args['spec-version']
+
+  const enhancements = loadEnhancements(args.enhancements)
+
+  console.log(`Parsing CDDL: ${cddlPath}`)
+  const ast = parse(cddlPath)
+  console.log(`  ${ast.length} top-level definitions`)
Evidence
PR Compliance ID 11 requires validating required inputs/artifacts in automation scripts. The
generator logs Parsing CDDL and calls parse(cddlPath) without an existence/readability check, so
a missing/bad --cddl path will fail without an explicit, prevalidated error.

javascript/selenium-webdriver/generate_bidi.mjs[171-179]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`generate_bidi.mjs` resolves `--cddl` to `cddlPath` and immediately calls `parse(cddlPath)` without checking whether the file exists/is readable. In CI/Bazel actions this can surface as an opaque error and may not clearly indicate the missing/incorrect input.

## Issue Context
Compliance requires validating preconditions and failing safely in automation scripts.

## Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[171-179]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. Useless entries.length >= 0 assert 📘 Rule violation ≡ Correctness
Description
The onJavascriptLog and onBeforeRequestSent tests include tautological assertions
(entries.length >= 0 and event.request.headers.length >= 0) that always pass and therefore do
not validate the behavior or payload they claim to cover, reducing overall test effectiveness. This
conflicts with PR Compliance ID 6’s requirement to add appropriate tests for changes.
Code

javascript/selenium-webdriver/test/bidi/generated/log_test.js[R174-189]

+    describe('onJavascriptLog', function () {
+      it('can listen to javascript log entries', async function () {
+        const entries = []
+
+        await log.onJavascriptLog((params) => {
+          entries.push(params)
+        })
+
+        await driver.get(Pages.logEntryAdded)
+
+        // Trigger a JS error to produce a javascript log entry
+        await driver.findElement({ id: 'jsException' }).click()
+
+        // onJavascriptLog covers JS-level log entries
+        assert.ok(entries.length >= 0) // may or may not fire depending on driver impl
+      })
Evidence
PR Compliance ID 6 requires tests that meaningfully validate behavior changes. In the cited tests,
assert.ok(entries.length >= 0) and assert.ok(event.request.headers.length >= 0) are always true
for any array-like value (including empty arrays), so these assertions will pass regardless of
whether the generated BiDi log/network events are correct, demonstrating that the tests do not
actually verify the expected functionality or correctness of the received payload.

AGENTS.md: Add or update tests for changes; prefer small unit tests and avoid mocks
javascript/selenium-webdriver/test/bidi/generated/log_test.js[174-189]
javascript/selenium-webdriver/test/bidi/generated/network_test.js[56-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Replace tautological assertions in the `onJavascriptLog` and `onBeforeRequestSent` generated BiDi tests that currently check only that `.length >= 0`, because these always pass and do not verify correct behavior or payload structure.

## Issue Context
PR Compliance ID 6 expects tests to validate functionality and behavior changes; assertions that are always true weaken coverage and can allow regressions in generated log and network event handling to slip through undetected.

## Fix Focus Areas
- javascript/selenium-webdriver/test/bidi/generated/log_test.js[174-189]
- javascript/selenium-webdriver/test/bidi/generated/network_test.js[56-60]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Generated comments stripped 🐞 Bug ⚙ Maintainability
Description
The Bazel TypeScript compilation for generated BiDi modules uses tsc --removeComments, which
strips the generator’s injected license header and JSDoc @deprecated documentation from emitted
artifacts. This defeats the generator’s explicit intent to embed these comments and reduces
maintainability/documentation quality in the published package.
Code

javascript/selenium-webdriver/private/generate_bidi.bzl[R39-45]

+    args = ctx.actions.args()
+    args.add("--target", "ES2020")
+    args.add("--module", "NodeNext")
+    args.add("--moduleResolution", "NodeNext")
+    args.add("--declaration")
+    args.add("--removeComments")
+    args.add("--outDir", js_outputs[0].dirname)
Evidence
The generator explicitly defines a license comment block and the manifest injects JSDoc comments,
but the build rule compiles with --removeComments, which strips them from generated outputs.

javascript/selenium-webdriver/private/generate_bidi.bzl[39-45]
javascript/selenium-webdriver/generate_bidi.mjs[620-636]
javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[2-7]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Generated BiDi TS files intentionally include a license header and enhancement-injected JSDoc (e.g., `@deprecated`). The custom Bazel `tsc` invocation uses `--removeComments`, which removes these comments from emitted outputs.

### Issue Context
- The generator emits `LICENSE_HEADER` into every generated domain file.
- The enhancements manifest injects JSDoc `@deprecated` wrappers/types.
- `--removeComments` removes this documentation from outputs (and may also impact `.d.ts` comment retention depending on compiler behavior).

### Fix Focus Areas
- javascript/selenium-webdriver/private/generate_bidi.bzl[39-45]
- javascript/selenium-webdriver/generate_bidi.mjs[620-636]
- javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[2-7]

### Proposed fix
Remove `args.add("--removeComments")` from the generated BiDi compilation rule so that:
- The license header remains present in distributed generated artifacts.
- Deprecation JSDoc remains available to users and tooling.

If size is a concern, consider a separate minification/strip step at packaging time rather than stripping at compile time.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


15. Unbounded navigate to invalid host 📘 Rule violation ☼ Reliability
Description
The new test navigates to http://doesnotexist.invalid/ using BiDi `browsingContext.navigate(...,
wait: 'complete')` without any explicit timeout/cancellation, which can hang or become
timing-dependent based on DNS/network behavior and BiDi readiness semantics. This can delay the
suite (potentially up to the transport send timeout) and cause non-deterministic CI failures,
violating the requirement for deterministic failure paths in async/network operations.
Code

javascript/selenium-webdriver/test/bidi/generated/network_test.js[R114-116]

+        await browsingContext
+          .navigate({ context: contextId, url: 'http://doesnotexist.invalid/', wait: 'complete' })
+          .catch(() => {})
Evidence
PR Compliance ID 11 requires timeouts/cancellation for async waits and network I/O, yet the added
test code performs an awaited navigation to an intentionally invalid host (doesnotexist.invalid)
with wait: 'complete' and no bounding timeout/cancellation wrapper. Because the test awaits
navigate(...) before it waits for errorEvent, any delayed or never-returning navigation blocks
the rest of the test (making the later 5s driver.wait(...) ineffective), and in the existing BiDi
implementation the underlying send() has a ~30s command timeout, meaning the stall can last up to
~30 seconds per run and contribute to flakiness/timeouts.

javascript/selenium-webdriver/test/bidi/generated/network_test.js[114-116]
javascript/selenium-webdriver/test/bidi/generated/network_test.js[105-121]
javascript/selenium-webdriver/bidi/browsingContext.js[162-180]
javascript/selenium-webdriver/bidi/index.js[21-21]
javascript/selenium-webdriver/bidi/index.js[200-204]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A test performs an awaited BiDi navigation to a non-existent host using `wait: 'complete'` without any explicit timeout/cancellation, so the test can hang or block for a long time (up to the transport/command timeout) before it can assert the expected fetch error event.

## Issue Context
Compliance requires deterministic failure paths (timeouts/cancellation) for async waits and network I/O. The current test intentionally navigates to `http://doesnotexist.invalid/` and swallows the navigation error, but it does not bound how long the navigation attempt itself can take; since `navigate()` is awaited before waiting for `errorEvent`, a stalled/never-completing navigation makes the later 5s `driver.wait(...)` ineffective and can slow/flake CI.

## Fix Focus Areas
- javascript/selenium-webdriver/test/bidi/generated/network_test.js[114-116]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. ws.on('message') listener leaks 📘 Rule violation ☼ Reliability
Description
Generated on<Event>() helpers add a new WebSocket message listener every time they’re called and
provide no unsubscribe/cleanup mechanism, leading to leaked listeners and duplicate callbacks over
time. The listener also blindly JSON.parses incoming frames without guarding or validation,
risking MaxListenersExceededWarning and unhandled exceptions/crashes on malformed or non-JSON data.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R902-918]

+  lines.push(`  async ${onMethodName}(callback: ${cbType}): Promise<void> {`)
+  lines.push(`    await this.bidi.subscribe('${methodStr}')`)
+  // Use the raw WebSocket message listener — the same pattern as the hand-coded
+  // logInspector.js / browsingContext.js classes.  bidi/index.js intentionally
+  // ignores WebSocket messages without a numeric "id" in its response dispatcher,
+  // so event payloads must be captured directly from the socket instead.
+  lines.push(`    const ws = this.bidi.socket`)
+  lines.push(`    ws.on('message', (data: { toString(): string }) => {`)
+  lines.push(`      const msg = JSON.parse(data.toString()) as {`)
+  lines.push(`        method?: string`)
+  lines.push(`        params?: unknown`)
+  lines.push(`      }`)
+  lines.push(`      if (msg.method === '${methodStr}') {`)
+  lines.push(`        callback(${paramsTypeName ? `msg.params as ${paramsTypeName}` : 'msg.params'})`)
+  lines.push(`      }`)
+  lines.push(`    })`)
+  lines.push(`  }`)
Evidence
PR Compliance ID 10 requires cleanup/rollback for async workflows that register state/resources, but
the new generateEventMethod emits code that registers a persistent WebSocket message listener
without any disposal path, so repeated on<Event>() calls accumulate listeners. PR Compliance ID 11
requires early validation of protocol-derived inputs and deterministic failures, yet the generated
handler trusts arbitrary WebSocket payloads and calls JSON.parse without try/catch or shape
checks, allowing malformed frames to throw asynchronously. The existing BiDi transport
(bidi/index.js) is cited as already documenting the MaxListenersExceededWarning risk and using a
single shared dispatcher with guarded JSON parsing, indicating the generated approach reintroduces
known failure modes.

javascript/selenium-webdriver/generate_bidi.mjs[902-918]
javascript/selenium-webdriver/generate_bidi.mjs[897-918]
javascript/selenium-webdriver/bidi/index.js[58-81]
javascript/selenium-webdriver/generate_bidi.mjs[909-916]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The generated `on…` event methods install a new `ws.on('message', ...)` listener on every call and never remove it, which can leak listeners, multiply callbacks, and trigger MaxListeners warnings over time. The handler also performs an unguarded `JSON.parse` on protocol-derived WebSocket frames (without try/catch or validation), which can throw on malformed/non-JSON payloads and crash or produce unclear asynchronous failures.

## Issue Context
This event-handling code is emitted into the generated BiDi domain classes and may be invoked repeatedly by consumers across many domains and events. The existing BiDi transport implementation in `bidi/index.js` already explains why adding many message listeners is problematic and uses a single shared dispatcher with guarded JSON parsing; the generator should align with that approach. Protocol/WebSocket payloads are external inputs and should be validated early to produce deterministic, actionable failures.

## Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[897-920]
- javascript/selenium-webdriver/bidi/index.js[58-81]
- javascript/selenium-webdriver/generate_bidi.mjs[909-916]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


17. Genrule uses unquoted args 📘 Rule violation ☼ Reliability
Description
The new genrule command constructs a shell invocation with $@ and $(SRCS) unquoted, which is
unsafe if paths contain spaces/special characters and can cause non-deterministic CI/tooling
behavior. Build/tooling scripts should use safe argument handling.
Code

javascript/selenium-webdriver/private/generate_bidi.bzl[R113-119]

+    native.genrule(
+        name = merged_name,
+        srcs = [cddl_file] + extra_cddl_files,
+        outs = [name + "_merged.cddl"],
+        cmd = "$(location " + merge_tool + ") $@ $(SRCS)",
+        tools = [merge_tool],
+    )
Evidence
PR Compliance ID 13 requires safe argument handling in build/CI/scripts/tooling. The added genrule
uses a raw cmd string with unquoted $@/$(SRCS), which is not robust to special characters in
paths.

javascript/selenium-webdriver/private/generate_bidi.bzl[113-119]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `native.genrule` command string passes `$@` and `$(SRCS)` without robust quoting/argument handling, which is a common source of brittle CI behavior.

## Issue Context
Compliance requires build/tooling glue to use safe argument handling; consider replacing the genrule with a small Starlark rule using `ctx.actions.run` so arguments are passed as an argv list.

## Fix Focus Areas
- javascript/selenium-webdriver/private/generate_bidi.bzl[113-119]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

18. excludeTypes can break imports 🐞 Bug ⚙ Maintainability
Description
The generator builds allGeneratedTypeNames and typeNameToDomain from the unfiltered
typesByDomain, then later applies enhancement.excludeTypes per domain; this can produce
cross-domain imports or method return types that reference types excluded from the emitting domain.
If excludeTypes is ever used, generated TS can fail to compile due to imports of non-exported
names or references to removed types.
Code

javascript/selenium-webdriver/generate_bidi.mjs[R187-194]

+  // Build a set of all exported type/interface names across all domains.
+  // Used by generateCommandMethod to detect when a result type doesn't exist
+  // in the generated output and should be treated as void.
+  const allGeneratedTypeNames = buildAllTypeNames(typesByDomain)
+
+  // Build a map from type name to its domain for cross-domain import generation.
+  const typeNameToDomain = buildTypeNameToDomainMap(typesByDomain)
+
Evidence
The global type maps are computed once from the unfiltered typesByDomain, but per-domain type
output is later filtered via excludeTypes prior to computing imports; the manifest format already
exposes excludeTypes (currently empty), so this can become a real failure once used.

javascript/selenium-webdriver/generate_bidi.mjs[187-223]
javascript/selenium-webdriver/generate_bidi.mjs[730-739]
javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[2-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The generator constructs global type maps (`allGeneratedTypeNames`, `typeNameToDomain`) before applying `enhancement.excludeTypes` filtering, so those maps can become stale relative to emitted type blocks.

### Issue Context
Today’s manifest sets `excludeTypes: []` everywhere, so the bug is latent; however, `excludeTypes` is an advertised customization mechanism and will be error-prone when used.

### Fix Focus Areas
- javascript/selenium-webdriver/generate_bidi.mjs[187-223]
- javascript/selenium-webdriver/generate_bidi.mjs[730-739]

### Suggested fix
Before building `allGeneratedTypeNames` and `typeNameToDomain`, pre-apply `excludeTypes` for every domain:
1. Build `typesByDomain`.
2. Create `filteredTypesByDomain` by running `filterExcludedTypes()` for each domain that has `enhancement.excludeTypes`.
3. Build `allGeneratedTypeNames` and `typeNameToDomain` from `filteredTypesByDomain`.
4. Use `filteredTypesByDomain[domainKey]` when emitting.

This ensures import generation and void-detection can’t reference excluded types.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 9af40fb

Results up to commit ec6cc4b


🐞 Bugs (2) 📘 Rule violations (3) 📎 Requirement gaps (0) 🎨 UX issues (0)


Action required
1. Empty context in back/forward 🐞 Bug ≡ Correctness
Description
The BrowsingContext enhancement methods back() and forward() always call traverseHistory with
context: '' (empty string), so these convenience methods will consistently fail with an invalid
browsing context id.
Code

javascript/selenium-webdriver/private/bidi_enhancements_manifest.json[R12-13]
...

Comment thread javascript/selenium-webdriver/private/bidi_enhancements_manifest.json Outdated
Comment thread javascript/selenium-webdriver/private/bidi_enhancements_manifest.json Outdated
Comment thread javascript/selenium-webdriver/private/bidi_enhancements_manifest.json Outdated
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 1, 2026

Code review by qodo was updated up to the latest commit 2dfef49

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 1, 2026

Code review by qodo was updated up to the latest commit 68d0f22

@AutomatedTester
Copy link
Copy Markdown
Member

@pujagani This looks good, can we make sure the AI reviews are correct and resolve accordingly?

@pujagani
Copy link
Copy Markdown
Contributor Author

pujagani commented Jun 4, 2026

Thank you for reviewing @AutomatedTester. Will do.

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 5ec2e30

- back()/forward(): accept a context id instead of hard-coding an empty
  string; delegate to traverseHistory({ context, delta: ±1 })
- continueWithAuth: replace username!/password! non-null assertions with
  an explicit undefined guard that throws a descriptive error
- continueWithAuth: capture bidi.send() response and apply the same
  response['type'] === 'error' check as generated methods, preventing
  silent failures
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 25da91e

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 4e9302d

@pujagani pujagani force-pushed the js-bidi-codegen branch 2 times, most recently from 411f358 to 4e9302d Compare June 4, 2026 10:56
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 4e9302d

Comment thread javascript/selenium-webdriver/bidi/index.js
@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 527724a

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 3c7f94f

@qodo-code-review
Copy link
Copy Markdown
Contributor

qodo-code-review Bot commented Jun 4, 2026

Code review by qodo was updated up to the latest commit 9af40fb

@AutomatedTester AutomatedTester merged commit c526eb2 into SeleniumHQ:trunk Jun 8, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-devtools Includes everything BiDi or Chrome DevTools related C-nodejs JavaScript Bindings C-py Python Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants