[js] Add binding-neutral BiDi schema with cddl2ts-gated fidelity#17700
[js] Add binding-neutral BiDi schema with cddl2ts-gated fidelity#17700titusfortner wants to merge 3 commits into
Conversation
PR Summary by QodoAdd binding-neutral BiDi schema generator with cddl2ts fidelity gate Description
Diagram
High-Level Assessment
Files changed (7)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
14 rules 1. Null-only types become unknown
|
Normalize the parsed BiDi AST and project a flat, binding-neutral schema
(commands + events + types) for the generated Ruby/Java/Python clients,
so each binding consumes one explicit artifact instead of re-deriving the
awkward CDDL shapes from the raw AST.
Normalizer (normalize_bidi_ast.mjs): hoist inline string-literal unions to
named enums, canonicalize variant-union params into self-contained variant
records, hoist inline records, and flatten group composition (base types +
Extensible). Projector (project_bidi_schema.mjs): map to a small vocabulary
(record/enum/union/alias + ref/primitive/const/list/map), preserving wire
names and nullability verbatim; generation validates and fails on dangling
refs or dropped commands/events.
Fidelity is gated against cddl2ts, an independent generator over the same
AST, comparing record fields, field types (optional/nullable/array) and
enum values; only one intentional difference remains (wire-faithful
namespaceURI). Wired as Bazel targets create-bidi-src_schema (output name
derived from the target) and the bidi-schema-{tests,diff-test}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5a7fca3 to
5ae00f7
Compare
|
Code review by qodo was updated up to the latest commit 5ae00f7 |
|
Code review by qodo was updated up to the latest commit 421fc38 |
There was a problem hiding this comment.
Pull request overview
Adds a binding-neutral WebDriver BiDi schema artifact (commands/events/types) generated from shared CDDL AST/model data, plus Bazel wiring and tests that gate schema fidelity against cddl2ts to prevent silent field/type loss across bindings.
Changes:
- Introduces a BiDi AST normalizer and schema projector that emit a flat schema with referential-integrity + completeness validation.
- Extends the Bazel BiDi generation pipeline to produce a shared
*_schema.jsonartifact with cross-binding visibility. - Adds mocha tests, including a differential “oracle” check comparing the generated schema to
cddl2tsoutput.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| javascript/selenium-webdriver/project_bidi_schema.mjs | Projects normalized AST + model into a flat schema and validates it (integrity + completeness). |
| javascript/selenium-webdriver/project_bidi_schema_test.mjs | Unit tests for schema projection and validation helpers. |
| javascript/selenium-webdriver/private/generate_bidi.bzl | Adds schema-generation step to the Bazel BiDi pipeline and exposes schema to other bindings. |
| javascript/selenium-webdriver/normalize_bidi_ast.mjs | Normalizes raw CDDL AST into canonical forms (hoisted enums/records, flattened unions/composition). |
| javascript/selenium-webdriver/normalize_bidi_ast_test.mjs | Unit tests for AST normalizer transforms. |
| javascript/selenium-webdriver/BUILD.bazel | Adds project_bidi_schema_script and a mocha test target for schema tooling + fidelity gate. |
| javascript/selenium-webdriver/bidi_schema_diff_test.mjs | Differential test comparing generated schema to cddl2ts as an oracle. |
| // A `null` keyword or a `nil` prelude ref in a union means the value may be null. | ||
| const isNullAlt = (e) => | ||
| e === 'null' || (e && typeof e === 'object' && e.Type === 'group' && PRELUDE[e.Value] === 'null') | ||
|
|
||
| function projectRef(type) { | ||
| const all = typeList(type) | ||
| const entries = all.filter((e) => !isNullAlt(e)) | ||
| const node = | ||
| entries.length > 1 | ||
| ? entries.every(isLiteral) | ||
| ? { enum: entries.map((e) => e.Value) } | ||
| : { union: entries.map(projectEntry) } | ||
| : projectEntry(entries[0]) | ||
| if (entries.length < all.length) node.nullable = true // a `null` alternative means the value may be null |
There was a problem hiding this comment.
1. Null-only types become unknown 🐞 Bug ≡ Correctness
In projectRef(), null and prelude nil alternatives are filtered out before projecting the
remaining type; if the input type is *only* null/nil, the remaining list is empty and the schema
emits { primitive: 'unknown', nullable: true } instead of a real null type. This can silently
corrupt schema fidelity for any CDDL field/alias defined as exactly null/nil, and the cddl2ts
diff check likely won’t catch it because it only asserts presence of nullability, not the underlying
primitive.
Agent Prompt
### Issue description
`projectRef()` removes `null`/`nil` alternatives via `isNullAlt()`, then projects `entries[0]`. When the type is exactly `null`/`nil` (no non-null alternatives), `entries` becomes empty and `projectEntry(undefined)` returns `{ primitive: 'unknown' }`, producing an incorrect schema node.
### Issue Context
- `PRELUDE.nil` is mapped to `'null'`, and `projectEntry()` can correctly project a `nil` group ref to `{ primitive: 'null' }`.
- But `isNullAlt()` strips it before projection, which is only correct when there is at least one non-null alternative.
### Fix Focus Areas
- javascript/selenium-webdriver/project_bidi_schema.mjs[73-87]
### Suggested fix
- In `projectRef(type)`, after computing `entries`, add a guard:
- If `entries.length === 0` and `all.length > 0`, return `{ primitive: 'null' }` (and **do not** set `nullable`), because the type is exactly null.
- Only set `node.nullable = true` when there is at least one non-null alternative (i.e., `entries.length > 0 && entries.length < all.length`).
- Optionally add/extend a unit test to cover `nil`-only and `null`-only projection cases.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 7c315df |
|
Shouldn't this be a draft until #17657 gets a 👍? |
|
@diemol #17657 is merged. Do you mean #17701? |
|
Yes, sorry. I meant that one. Thank you for clarifying. From that point of view, then we could move forward on this one. |
🔗 Related Issues
Builds on #17657 (shared BiDi CDDL ast/model artifacts).
💥 What does this PR do?
Generates a single, binding-neutral BiDi schema (commands + events + types) from the shared CDDL artifacts, so the non-JS bindings can create client generators that consume one explicit, normalized artifact instead of each re-deriving the native CDDL shapes from the raw AST.
That re-derivation is where the bindings currently diverge and silently drop data — inline string-literal enums, variant-union params, composed-in base-type fields, optional/nullable fields. The schema normalizes all of these once (hoists inline enums to named types, canonicalizes variant unions into self-contained records, flattens group composition, preserves wire names and nullability verbatim) and the generation step fails the build if anything is dropped or dangling.
It lands the artifact and its fidelity gate only; no binding consumes it yet.
🔧 Implementation Notes
(A | B) & commonintersection form, because the non-TS bindings can't express TS-style intersections cleanly. This duplicates common fields across variants but keeps each variant complete and the discriminator-conditional rule structural.bidi-ast.json/bidi-model.jsonare made package-private (dropping the cross-binding visibility added in [js] Expose BiDi CDDL ast and model as shared artifacts #17657), since the schema folds in the model and supersedes both for bindings. They remain internal inputs to the schema and to the existing JS generation. Nothing consumed the old visibility yet, so this changes a not-yet-used contract.🤖 AI assistance
💡 Additional Considerations
javascript/selenium-webdrivertojavascript/bidi-support, the additional wiring seemed out of scope for this PR🔄 Types of changes