123456789_123456789_123456789_123456789_123456789_

Class: BiDiGenerate::Schema Private

Relationships & Source Files
Inherits: Object
Defined in: rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb

Constant Summary

  • CHECKABLE_PRIMITIVES =

    The scalar primitives that carry an inbound type-check. A field with no primitive (a ref, const, or opaque value) gets no descriptor and is left unchecked — lenient, so a missed check fails open rather than a wrong strict default rejecting valid data.

    # File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1220
    %w[string number integer boolean].freeze
  • OPAQUE =

    Resolution for anything not modeled as a value type (scalar, enum, empty record). Frozen because it is shared across callers.

    # File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 914
    {ref: nil, list: false, rbs: 'untyped'}.freeze
  • PRIMITIVE_RBS =

    Every primitive the projector can emit maps to an RBS type. unknown is intentionally absent — the projector rejects it (an unhandled CDDL construct fails the build), so it never reaches here; any other unlisted primitive fails generation at scalar_rbs rather than slipping through as untyped.

    # File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1213
    {
      'string' => 'String', 'number' => 'Numeric', 'integer' => 'Integer', 'boolean' => 'bool', 'null' => 'nil'
    }.freeze

Class Method Summary

  • .new(schema) ⇒ Schema constructor Internal use only

    Vendor content (schema vendor.) is folded in beside the spec's: a vendor module's commands and events become a domain of their own, and its types resolve like spec types and are emitted in that module (see owned_types).

Instance Method Summary

  • #build_added_command(cmd, namespace, domain) Internal use only

    The vendor variant already scopes the command, so its name drops the namespace (moz:listExtensions → list_extensions); a name that would silently override a spec command fails generation instead.

  • #build_vendor_command(cmd, type_name, entry, namespace) Internal use only
  • #command_wrapper_refs(domain) Internal use only

    The domain's command param/result wrapper type names — the classes a command constructs (params) or parses its result into.

  • #commands_for(domain) Internal use only
  • #domain_href(domain) Internal use only

    The domain's #module- spec link, or nil when the schema has none.

  • #domain_relative_path(name) Internal use only

    The class path to a type relative to its domain class (an accessor body resolves in the Domain subclass scope): "ExtensionData", or "AccessibilityLocator::Value" for a synthetic nested under its owner.

  • #domains Internal use only

    Domains that carry a command or event each become one generated module.

  • #enums_for(domain) Internal use only

    Enum types declared under "." become nested constant modules.

  • #envelope_synthetic?(type) ⇒ Boolean Internal use only

    A synthetic record lifted out as an envelope's params (its owner is an envelope).

  • #error_codes Internal use only

    The protocol-root ErrorCode enum's wire values (e.g. "no such frame"), in schema order.

  • #events_for(domain) Internal use only
  • #message_envelope?(type) ⇒ Boolean Internal use only

    A protocol message envelope is a record with a baked method discriminator ({method: <const>, params: …}) — the wire shape of a command/event message.

  • #owned_types(domain) Internal use only

    The types a domain's module declares.

  • #params_for(params_ref) Internal use only

    Flat params for a command: the record's fields, or — for a union of records — the merged superset of variant fields.

  • #plainly_reached_types Internal use only

    Type names reached by at least one non-union-arm reference: used as a record field, list element, map value, or alias target somewhere — not solely as a named union's variant.

  • #promote_command_params_records! Internal use only

    A command written in CDDL map form carries its params as an inline object (rather than the usual group form referencing a named params type).

  • #promote_to_domain_type!(name) Internal use only

    Strip the synthetic/owner/label tags so a lifted-out type emits as a top-level domain record instead of nesting under its (suppressed) envelope.

  • #ruby_path_for(name) Internal use only

    Public ruby-path resolver (Owner::Label for a synthetic), matching how a variant's ref is emitted — so a caller can map a variant ref back to its emitted record.

  • #structured_ref(name) Internal use only

    The Protocol-relative class path a command result parses into, or nil when it is non-structured (or a bare list, returned raw).

  • #suppressed_record?(type) ⇒ Boolean Internal use only

    Records the generator deliberately does not emit: a message envelope, or a synthetic params record lifted out of one.

  • #type_kind(ref) Internal use only
  • #type_prefixes(domain) Internal use only
  • #types_for(domain) Internal use only

    Structured value classes (records + discriminated unions) declared under "." Empty records are projector artifacts with nothing to carry, so they stay opaque hashes; only non-empty records and unions become classes.

  • #vendor_added_commands(domain, namespace) Internal use only

    The commands a vendor defines in a spec domain (webExtension.moz:listExtensions).

  • #vendor_domain?(domain) ⇒ Boolean Internal use only

    A vendor module's domain keeps its wire prefix (moz:debugging); a spec domain has none.

  • #vendor_events Internal use only

    A vendor event in a spec domain would need the same vendor-variant routing as a command; none exists yet, so fail generation rather than leak it into the shared class.

  • #vendor_messages(kind) Internal use only
  • #vendor_modules_for(domain) Internal use only

    The vendor modules a domain carries, one per namespace (moz → module Moz).

  • #vendor_param(field, namespace, taken) Internal use only

    A vendor field's ruby name drops its namespace prefix (moz:permanent → permanent): the module already scopes it, so re-encoding the namespace in every identifier is redundant.

  • #vendor_types_reached(domain) Internal use only

    The vendor types a vendor-added command in this spec domain reaches through its params and result, so they are emitted in the domain's module beside the command that returns them.

  • #annotate_discriminator_enum!(params, selector) private Internal use only

    A discriminated union's by field is validated against the whole allowed set: the const values that tag each variant plus the default variant's own enum values (e.g. continueWithAuth.action = provideCredentials + cancel).

  • #baked_discriminator?(field) ⇒ Boolean private Internal use only

    A const field is a baked discriminator tag, unless it is also nullable: the spec's literal | null (browsingContext.setBypassCSP, emulation.setScriptingEnabled) is a settable value (the literal to set, null to clear), so it stays a normal field that can serialize null rather than a fixed tag that can only ever emit the literal.

  • #default_variant_enum_values(selector, by) private Internal use only
  • #directionality(name) private Internal use only

    The type's send/receive tags (schema outbound/inbound) as constructor kwargs, coerced to plain booleans — shared by every structured-type builder.

  • #discriminated_variants(selector) private Internal use only
  • #domain_path(name) private Internal use only
  • #enum_const(field_type) private Internal use only

    The allowed-values constant path when a field (or a list's element) is an enum type, else nil.

  • #extensible_record?(name) ⇒ Boolean private Internal use only
  • #field_ir(field, extensible) private Internal use only
  • #field_name(field, extensible) private Internal use only
  • #guard_union_dispatch_keys_simple!(selector, ref) private Internal use only

    Union.build matches the command's kwargs to the selector's dispatch keys by symbol, which holds only while each dispatch wire key equals its ruby kwarg.

  • #leaf_const(node, seen = {}) private Internal use only

    The literal value of a const field, following alias chains, so the runtime can reject a value that is neither the literal nor null (a literal / null param such as emulation.setScriptingEnabled's enabled).

  • #leaf_primitive(node, seen = {}) private Internal use only

    The runtime-checkable scalar primitive of a field, following alias chains so a scalar hidden behind a named alias (js-uint -> integer, browsingContext.BrowsingContext -> string) is typed rather than opaque.

  • #merged_params(variant_fields, extensible) private Internal use only

    Merge variant field lists into one flat param superset.

  • #named_type(name) private Internal use only

    A named structured type's serialization ref (nil for a dotless/global type, never emitted as a class) and its absolute RBS class path, derived independently so each output keeps its own treatment of dotless names.

  • #named_union(name) private Internal use only

    Like named_type, but a union is typed to its value alias (variant | variant | …), not its class — the class has no instances, so a variant must be assignable where the union is expected.

  • #nilable(type, flag) private Internal use only
  • #ordered_variants(selector) private Internal use only
  • #plain_refs(node) private Internal use only

    The refs a node exposes through a NON-arm position: a record's fields and map value, or an alias's target.

  • #rbs_abs(path) private Internal use only
  • #rbs_const(value) private Internal use only
  • #rbs_type(node) private Internal use only
  • #record_class(name, type) private Internal use only
  • #record_params(fields, extensible) private Internal use only
  • #refs_in_type(node) private Internal use only

    Every type name a type expression references (mirrors the projector's refsInType), descending list element, map value, inline union arms, and inline record fields.

  • #resolve(node) private Internal use only

    Projects a schema type node to list:, nullable:, rbs:.

  • #resolve_named(name, seen = {}) private Internal use only

    Resolves a named ref to the same list:, rbs: facts, transparently following aliases — including alias-to-list — so an element type behind an alias (e.g. script.ListLocalValue -> [script.LocalValue]) is preserved.

  • #resolve_named_alias(name, inner, seen) private Internal use only
  • #resolve_union(node, nullable) private Internal use only

    An inline union of one union-typed arm plus scalars (e.g. a MappingRemoteValue entry, RemoteValue / string) is carried as that union ref so nested entries are typed.

  • #ruby_path(name) private Internal use only

    Class path, nesting a synthetic type under its owner as Owner::Label so a ref resolves to the same nested constant the type is emitted as.

  • #scalar_rbs(node) private Internal use only

    The leaf of #resolve: the bare scalar type, before any nullable wrap.

  • #union_alias_path(name) private Internal use only

    Absolute RBS path of a union's value alias: its class path with the last segment snake-cased (WebExtension::ExtensionData -> ...::WebExtension::extension_data), matching the type alias emitted alongside the class.

  • #union_class(name) private Internal use only
  • #union_from_alias(name) private Internal use only

    The sole alias-union is input.Origin ("viewport" | "pointer" | ElementOrigin): a scalar-or-object union the object-payload selector model doesn't cover, so the projector leaves it an alias with no selector.

  • #union_from_selector(name, selector) private Internal use only

    Map a union selector to dispatch variants the template renders:

  • #union_params(type, ref = nil) private Internal use only

    Merge a union's record variants into one flat param list for the command signature.

  • #union_ref?(name) ⇒ Boolean private Internal use only

    True when a ref (following aliases) is a union — the only arm whose from_json tolerates a scalar sibling.

  • #vendor_extended?(name) ⇒ Boolean private Internal use only

Constructor Details

.new(schema) ⇒ Schema

Vendor content (schema vendor.) is folded in beside the spec's: a vendor module's commands and events become a domain of their own, and its types resolve like spec types and are emitted in that module (see owned_types).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 573

def initialize(schema)
  @vendor = schema['vendor'] || {}
  @vendor_types = @vendor.values.map { |section| section['types'] || {} }.reduce({}, :merge)
  @types = schema['types'].merge(@vendor_types)
  @commands = schema['commands'] + vendor_messages('commands').select { |c| vendor_domain?(c['domain']) }
  @events = schema['events'] + vendor_events
  @domains = schema['domains'] || {}
  promote_command_params_records!
end

Instance Method Details

#annotate_discriminator_enum!(params, selector) (private)

A discriminated union's by field is validated against the whole allowed set: the const values that tag each variant plus the default variant's own enum values (e.g. continueWithAuth.action = provideCredentials + cancel). That spans variants, so no single enum constant fits — emit an inline symbol=>wire hash so the check accepts the idiomatic symbol like every other enum. Boolean discriminators (handleRequestDevicePrompt.accept) need no membership check.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1276

def annotate_discriminator_enum!(params, selector)
  by = selector['by']
  tagged = by ? selector['variants'].map { |v| v['value'] } : []
  return unless !tagged.empty? && tagged.all?(String)

  allowed = (tagged + default_variant_enum_values(selector, by)).uniq
  pairs = allowed.map { |v| "#{BiDiGenerate.enum_key(v)}: '#{v}'" }
  param = params.find { |p| p.wire_name == by }
  return unless param

  param.enum = "{#{pairs.join(', ')}}"
  param.rbs = 'Symbol'
end

#baked_discriminator?(field) ⇒ Boolean (private)

A const field is a baked discriminator tag, unless it is also nullable: the spec's literal | null (browsingContext.setBypassCSP, emulation.setScriptingEnabled) is a settable value (the literal to set, null to clear), so it stays a normal field that can serialize null rather than a fixed tag that can only ever emit the literal.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1064

def baked_discriminator?(field)
  field['type'].key?('const') && !field['type']['nullable']
end

#build_added_command(cmd, namespace, domain)

The vendor variant already scopes the command, so its name drops the namespace (moz:listExtensions → list_extensions); a name that would silently override a spec command fails generation instead.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 699

def build_added_command(cmd, namespace, domain)
  command = BiDiGenerate.build_command(self, cmd.merge('name' => cmd['name'].delete_prefix("#{namespace}:")))
  spec_names = commands_for(domain).map do |c|
    BiDiGenerate.safe_method_name(BiDiGenerate.camel_to_snake(c['name']))
  end
  if spec_names.include?(command.method_name)
    raise "vendor command #{cmd['method']} would override spec command #{domain}.#{command.method_name}"
  end

  command
end

#build_vendor_command(cmd, type_name, entry, namespace)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 711

def build_vendor_command(cmd, type_name, entry, namespace)
  shared = record_params(@types[type_name]['fields'], extensible_record?(type_name))
  taken = shared.map(&:ruby_name)
  VendorCommand.new(
    method_name: BiDiGenerate.safe_method_name(BiDiGenerate.camel_to_snake(cmd['name'])),
    wire_name: cmd['method'],
    result_ref: cmd['result'] && structured_ref(cmd['result']['ref']),
    params_class: BiDiGenerate.type_class_name(type_name),
    shared_params: shared,
    vendor_params: entry['fields'].map { |field| vendor_param(field, namespace, taken) },
    spec_href: cmd['specHref']
  )
end

#command_wrapper_refs(domain)

The domain's command param/result wrapper type names — the classes a command constructs (params) or parses its result into. They are reachable (so tagged outbound/inbound) but are the message wrappers a command method already builds, not data a caller composes, so they are excluded from the type accessors.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 653

def command_wrapper_refs(domain)
  commands_for(domain).flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact.to_set
end

#commands_for(domain)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 645

def commands_for(domain)
  @commands.select { |c| c['domain'] == domain }
end

#default_variant_enum_values(selector, by) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1290

def default_variant_enum_values(selector, by)
  default = selector['default']
  field = default && @types[default]['fields'].find { |f| f['wire'] == by }
  ref = field && field['type']['ref']
  ref && @types[ref] && @types[ref]['kind'] == 'enum' ? @types[ref]['values'] : []
end

#directionality(name) (private)

The type's send/receive tags (schema outbound/inbound) as constructor kwargs, coerced to plain booleans — shared by every structured-type builder.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1024

def directionality(name)
  node = @types[name]
  {outbound: node['outbound'] ? true : false, inbound: node['inbound'] ? true : false}
end

#discriminated_variants(selector) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1154

def discriminated_variants(selector)
  variants = selector['variants'].map do |variant|
    VariantIR.new(mode: :value, value: variant['value'], ref: ruby_path(variant['ref']), requires: nil)
  end
  return variants unless selector['default']

  variants << VariantIR.new(mode: :fallback, value: nil, ref: ruby_path(selector['default']), requires: nil)
end

#domain_href(domain)

The domain's #module- spec link, or nil when the schema has none.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 609

def domain_href(domain)
  @domains.dig(domain, 'specHref')
end

#domain_path(name) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 872

def domain_path(name)
  name.include?('.') ? ruby_path(name) : nil
end

#domain_relative_path(name)

The class path to a type relative to its domain class (an accessor body resolves in the Domain subclass scope): "ExtensionData", or "AccessibilityLocator::Value" for a synthetic nested under its owner.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 671

def domain_relative_path(name)
  prefix = "#{BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(name.split('.', 2).first))}::"
  ruby_path(name).sub(/\A#{Regexp.escape(prefix)}/, '')
end

#domains

Domains that carry a command or event each become one generated module.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 641

def domains
  (@commands + @events).map { |entry| entry['domain'] }.uniq
end

#enum_const(field_type) (private)

The allowed-values constant path when a field (or a list's element) is an enum type, else nil. Union command-params skip this (their merged superset can blur a discriminator's const vs enum); only flat record params get the outbound check.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1247

def enum_const(field_type)
  ref = field_type['ref'] || field_type.dig('list', 'ref')
  return unless ref && @types[ref] && @types[ref]['kind'] == 'enum'

  BiDiGenerate.enum_const_path(ref)
end

#enums_for(domain)

Enum types declared under "." become nested constant modules.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 767

def enums_for(domain)
  owned_types(domain).filter_map do |name, type|
    next unless type['kind'] == 'enum'

    pairs = type['values'].map { |v| [BiDiGenerate.enum_key(v), v] }
    Enum.new(constant_name: BiDiGenerate.screaming_snake(name.split('.', 2).last), pairs: pairs,
             primitive: type['primitive'], spec_href: type['specHref'])
  end
end

#envelope_synthetic?(type) ⇒ Boolean

A synthetic record lifted out as an envelope's params (its owner is an envelope).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 852

def envelope_synthetic?(type)
  return false unless type['synthetic']

  owner = @types[type['owner']]
  owner && owner['kind'] == 'record' && message_envelope?(owner)
end

#error_codes

The protocol-root ErrorCode enum's wire values (e.g. "no such frame"), in schema order. Used to generate the BiDi-specific Error subclasses. [] when the schema has no ErrorCode.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 818

def error_codes
  @types.dig('ErrorCode', 'values') || []
end

#events_for(domain)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 746

def events_for(domain)
  @events.select { |e| e['domain'] == domain }
end

#extensible_record?(name) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1052

def extensible_record?(name)
  @types.dig(name, 'extensible') || vendor_extended?(name) ? true : false
end

#field_ir(field, extensible) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1068

def field_ir(field, extensible)
  resolved = resolve(field['type'])
  ruby_name = field_name(field, extensible)
  FieldIR.new(ruby_name: ruby_name, wire_key: field['wire'],
              required: field['required'], nullable: resolved[:nullable],
              ref: resolved[:ref], list: resolved[:list], enum: enum_const(field['type']),
              primitive: leaf_primitive(field['type']), scalar: resolved[:scalar],
              const: leaf_const(field['type']), rbs: resolved[:rbs])
end

#field_name(field, extensible) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1056

def field_name(field, extensible)
  BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(field['name']), extensible: extensible)
end

#guard_union_dispatch_keys_simple!(selector, ref) (private)

Union.build matches the command's kwargs to the selector's dispatch keys by symbol, which holds only while each dispatch wire key equals its ruby kwarg. Every current key is a single lowercase word; fail generation if a new one is camelCase so the outbound dispatch gets an explicit wire<->ruby mapping then.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1313

def guard_union_dispatch_keys_simple!(selector, ref)
  keys = selector['by'] ? [selector['by']] : (selector['ordered'] || []).flat_map { |arm| arm['requires'] }
  camel = keys.reject { |k| BiDiGenerate.camel_to_snake(k) == k }
  return if camel.empty?

  raise "union command param #{ref} dispatches on non-snake wire key(s) #{camel.inspect}; " \
        'Union.build matches kwargs to dispatch keys by symbol, so give the outbound ' \
        'dispatch an explicit wire<->ruby mapping before shipping this.'
end

#leaf_const(node, seen = {}) (private)

The literal value of a const field, following alias chains, so the runtime can reject a value that is neither the literal nor null (a literal / null param such as emulation.setScriptingEnabled's enabled). Nil for any non-const node — const literals are never nil, so nil unambiguously means "no const" (a null value is carried by nullable).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1082

def leaf_const(node, seen = {})
  return node['const'] if node.key?('const')
  return nil unless node.key?('ref')

  name = node['ref']
  type = @types[name]
  return nil if seen[name] || type.nil? || type['kind'] != 'alias'

  seen[name] = true
  leaf_const(type['type'], seen)
end

#leaf_primitive(node, seen = {}) (private)

The runtime-checkable scalar primitive of a field, following alias chains so a scalar hidden behind a named alias (js-uint -> integer, browsingContext.BrowsingContext -> string) is typed rather than opaque. The projector carries the primitive on the alias node; this surfaces it onto the field. Nil for a list (its elements are not scalar-checked), a record/union ref, an enum, a const, or an opaque value.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1099

def leaf_primitive(node, seen = {})
  return node['primitive'] if node.key?('primitive') && CHECKABLE_PRIMITIVES.include?(node['primitive'])
  return nil unless node.key?('ref')

  name = node['ref']
  type = @types[name]
  return nil if seen[name] || type.nil? || type['kind'] != 'alias'

  seen[name] = true
  leaf_primitive(type['type'], seen)
end

#merged_params(variant_fields, extensible) (private)

Merge variant field lists into one flat param superset. A field is required only when every variant declares it required; variant-specific fields become optional.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1299

def merged_params(variant_fields, extensible)
  all_fields = variant_fields.flatten
  all_fields.map { |f| f['wire'] }.uniq.map do |wire|
    field = all_fields.find { |f| f['wire'] == wire }
    required = variant_fields.all? { |fields| fields.any? { |f| f['wire'] == wire && f['required'] } }
    Param.new(ruby_name: field_name(field, extensible),
              wire_name: wire, required: required, rbs: rbs_type(field['type']))
  end
end

#message_envelope?(type) ⇒ Boolean

A protocol message envelope is a record with a baked method discriminator ({method: <const>, params: …}) — the wire shape of a command/event message. No value type carries a const method field, so this is unambiguous.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 847

def message_envelope?(type)
  type['fields'].any? { |f| f['wire'] == 'method' && f['type'].key?('const') }
end

#named_type(name) (private)

A named structured type's serialization ref (nil for a dotless/global type, never emitted as a class) and its absolute RBS class path, derived independently so each output keeps its own treatment of dotless names.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 986

def named_type(name)
  {ref: domain_path(name), list: false, rbs: rbs_abs(ruby_path(name))}
end

#named_union(name) (private)

Like named_type, but a union is typed to its value alias (variant | variant | …), not its class — the class has no instances, so a variant must be assignable where the union is expected. The serialization ref is unchanged (still the union that dispatches inbound).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 993

def named_union(name)
  {ref: domain_path(name), list: false, rbs: union_alias_path(name)}
end

#nilable(type, flag) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1018

def nilable(type, flag)
  flag ? BiDiGenerate.rbs_nilable(type) : type
end

#ordered_variants(selector) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1163

def ordered_variants(selector)
  (selector['ordered'] || []).map do |arm|
    VariantIR.new(mode: :presence, value: nil, ref: ruby_path(arm['ref']), requires: arm['requires'])
  end
end

#owned_types(domain)

The types a domain's module declares. A spec domain's are the "." types the spec defines; a vendor's helper types under that prefix are not emitted until a generated command needs them. A vendor module (moz:debugging) owns the vendor types its commands and events name (Mozilla prefixes them mozDebugging.), read off their params and result refs.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 781

def owned_types(domain)
  vendor = vendor_domain?(domain)
  prefixes = type_prefixes(domain).map { |prefix| "#{prefix}." }
  added = vendor ? Set.new : vendor_types_reached(domain)
  @types.select do |name, _|
    next false unless prefixes.any? { |p| name.start_with?(p) }

    @vendor_types.key?(name) ? vendor || added.include?(name) : !vendor
  end
end

#params_for(params_ref)

Flat params for a command: the record's fields, or — for a union of records — the merged superset of variant fields. Returns [] for commands with no params, or nil when params can't be flattened (alias, or a union whose variants aren't all records) so the caller forwards verbatim.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 754

def params_for(params_ref)
  return [] unless params_ref

  type = @types[params_ref['ref']]
  return nil unless type

  case type['kind']
  when 'record' then record_params(type['fields'], extensible_record?(params_ref['ref']))
  when 'union' then union_params(type, params_ref['ref'])
  end
end

#plain_refs(node) (private)

The refs a node exposes through a NON-arm position: a record's fields and map value, or an alias's target. A named union contributes none — its variants are arm positions (built through the union), so they do not count toward plainly_reached_types.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 879

def plain_refs(node)
  case node['kind']
  when 'record'
    refs = node['fields'].flat_map { |f| refs_in_type(f['type']) }
    node['map'] ? refs + refs_in_type(node['map']) : refs
  when 'alias' then refs_in_type(node['type'])
  else []
  end
end

#plainly_reached_types

Type names reached by at least one non-union-arm reference: used as a record field, list element, map value, or alias target somewhere — not solely as a named union's variant. A type reached only as a union arm is built through its union (a variant factory or the command's flattened dispatch), so a nested one needs no accessor; one reached as a plain field ref (browsingContext.AccessibilityLocator's value) does.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 662

def plainly_reached_types
  @plainly_reached_types ||= @types.each_value.with_object(Set.new) do |node, reached|
    plain_refs(node).each { |ref| reached << ref }
  end
end

#promote_command_params_records!

A command written in CDDL map form carries its params as an inline object (rather than the usual group form referencing a named params type). The projector links the command to those params, but hoists them into a synthetic record owned by the command's message envelope. That envelope is suppressed (Transport forms it), so the synthetic params record would never be emitted even though the command's params ref points straight at it. Promote it to a top-level domain record so the generator emits and references it like any other params type. Today this is exactly userAgentClientHints.setClientHintsOverride.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 621

def promote_command_params_records!
  @commands.each do |cmd|
    ref = cmd.dig('params', 'ref')
    next unless ref

    type = @types[ref]
    promote_to_domain_type!(ref) if type && envelope_synthetic?(type)
  end
end

#promote_to_domain_type!(name)

Strip the synthetic/owner/label tags so a lifted-out type emits as a top-level domain record instead of nesting under its (suppressed) envelope.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 633

def promote_to_domain_type!(name)
  type = @types[name]
  type&.delete('synthetic')
  type&.delete('owner')
  type&.delete('label')
end

#rbs_abs(path) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1240

def rbs_abs(path)
  "::Selenium::WebDriver::BiDi::Protocol::#{path}"
end

#rbs_const(value) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1231

def rbs_const(value)
  case value
  when true, false then 'bool'
  when ::String then 'String'
  when ::Numeric then 'Numeric'
  else 'untyped'
  end
end

#rbs_type(node) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1205

def rbs_type(node)
  resolve(node)[:rbs]
end

#record_class(name, type) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1029

def record_class(name, type)
  extensible = extensible_record?(name)
  const = type['fields'].find { |f| baked_discriminator?(f) }
  discriminator = const && {ruby_name: field_name(const, extensible),
                            wire: const['wire'], value: const['type']['const'],
                            rbs: rbs_const(const['type']['const'])}
  fields = type['fields'].reject { |f| baked_discriminator?(f) }.map { |f| field_ir(f, extensible) }
  # Every extensible type gets the extensions store: an undeclared wire key is preserved
  # and echoed back on any type the spec marks extensible, whether or not it is re-sendable.
  # Extensibility alone is the signal; send-reachability does not enter into it. A type a
  # vendor extends is open too: its vendor variant composes the vendor fields through that
  # store (see VendorCommand), which a closed record would reject.
  TypeClass.new(ruby_name: BiDiGenerate.type_class_name(name), fields: fields,
                discriminator: discriminator, extensible: extensible,
                schema_name: name, synthetic: type['synthetic'] ? true : false,
                owner: type['owner'], label: type['label'], spec_href: type['specHref'],
                **directionality(name))
end

#record_params(fields, extensible) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1193

def record_params(fields, extensible)
  fields.map do |field|
    Param.new(
      ruby_name: field_name(field, extensible),
      wire_name: field['wire'],
      required: field['required'],
      enum: enum_const(field['type']),
      rbs: rbs_type(field['type'])
    )
  end
end

#refs_in_type(node) (private)

Every type name a type expression references (mirrors the projector's refsInType), descending list element, map value, inline union arms, and inline record fields. An inline union arm inside a field is a plain position — the field is filled with it.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 892

def refs_in_type(node)
  return [] unless node
  return [node['ref']] if node['ref']
  return refs_in_type(node['list']) if node['list']
  return refs_in_type(node['map']) if node['map']
  return node['union'].flat_map { |arm| refs_in_type(arm) } if node['union']
  return node['record'].flat_map { |f| refs_in_type(f['type']) } if node['record']

  []
end

#resolve(node) (private)

Projects a schema type node to list:, nullable:, rbs:. Deriving the serialization facts and the RBS signature from one walk keeps them from drifting apart when the schema shape changes.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 919

def resolve(node)
  nullable = node['nullable'] ? true : false
  if node.key?('list')
    element = resolve(node['list'])
    return {ref: element[:ref], list: true, nullable: nullable, scalar: element[:scalar],
            rbs: nilable("Array[#{element[:rbs]}]", nullable)}
  end
  if node.key?('ref')
    named = resolve_named(node['ref'])
    return {ref: named[:ref], list: named[:list], nullable: nullable, scalar: named[:scalar],
            rbs: nilable(named[:rbs], nullable)}
  end
  return resolve_union(node, nullable) if node.key?('union')

  {ref: nil, list: false, nullable: nullable, rbs: nilable(scalar_rbs(node), nullable)}
end

#resolve_named(name, seen = {}) (private)

Resolves a named ref to the same list:, rbs: facts, transparently following aliases — including alias-to-list — so an element type behind an alias (e.g. script.ListLocalValue -> [script.LocalValue]) is preserved. Nullability is a property of the referencing node (applied by #resolve), so it is not threaded here. seen guards against cyclic ref-aliases.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 967

def resolve_named(name, seen = {})
  return OPAQUE if name.nil? || seen[name]

  seen[name] = true
  type = @types[name]
  return OPAQUE unless type

  case type['kind']
  when 'record' then type['fields'].empty? ? OPAQUE : named_type(name)
  when 'union' then named_union(name)
  when 'enum' then {ref: nil, list: false, rbs: 'Symbol'}
  when 'alias' then resolve_named_alias(name, type['type'], seen)
  else OPAQUE
  end
end

#resolve_named_alias(name, inner, seen) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1006

def resolve_named_alias(name, inner, seen)
  return named_union(name) if inner.key?('union')
  return resolve_named(inner['ref'], seen) if inner.key?('ref')

  if inner.key?('list')
    element = resolve(inner['list'])
    return {ref: element[:ref], list: true, scalar: element[:scalar], rbs: "Array[#{element[:rbs]}]"}
  end

  {ref: nil, list: false, rbs: scalar_rbs(inner)}
end

#resolve_union(node, nullable) (private)

An inline union of one union-typed arm plus scalars (e.g. a MappingRemoteValue entry, RemoteValue / string) is carried as that union ref so nested entries are typed. Because the union is object_only, a bare-scalar sibling would raise there — so the projector's scalar signal (a bare-scalar arm is present) is forwarded, and the runtime passes a non-object leaf through instead (the map's string keys). Any other shape (a record arm, multiple structured arms, all scalars) stays opaque.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 942

def resolve_union(node, nullable)
  refs = node['union'].select { |arm| arm.key?('ref') }
  opaque = {ref: nil, list: false, nullable: nullable, rbs: nilable('untyped', nullable)}
  return opaque unless refs.one? && union_ref?(refs.first['ref'])

  named = resolve_named(refs.first['ref'])
  {ref: named[:ref], list: named[:list], nullable: nullable, scalar: node['scalar'],
   rbs: nilable('untyped', nullable)}
end

#ruby_path(name) (private)

Class path, nesting a synthetic type under its owner as Owner::Label so a ref resolves to the same nested constant the type is emitted as.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 905

def ruby_path(name)
  type = @types[name]
  return BiDiGenerate.type_ruby_path(name) unless type && type['synthetic']

  "#{ruby_path(type['owner'])}::#{type['label']}"
end

#ruby_path_for(name)

Public ruby-path resolver (Owner::Label for a synthetic), matching how a variant's ref is emitted — so a caller can map a variant ref back to its emitted record.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 868

def ruby_path_for(name) = ruby_path(name)

#scalar_rbs(node) (private)

The leaf of #resolve: the bare scalar type, before any nullable wrap. An alias's own nullable is intentionally left off — only the referencing node's is applied.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1224

def scalar_rbs(node)
  return PRIMITIVE_RBS.fetch(node['primitive']) if node.key?('primitive')
  return rbs_const(node['const']) if node.key?('const')

  'untyped'
end

#structured_ref(name)

The Protocol-relative class path a command result parses into, or nil when it is non-structured (or a bare list, returned raw).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 861

def structured_ref(name)
  resolved = resolve_named(name)
  resolved[:list] ? nil : resolved[:ref]
end

#suppressed_record?(type) ⇒ Boolean

Records the generator deliberately does not emit: a message envelope, or a synthetic params record lifted out of one. Both are reachable only through the envelope, which Transport replaces — so nothing else references them.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 840

def suppressed_record?(type)
  message_envelope?(type) || envelope_synthetic?(type)
end

#type_kind(ref)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 742

def type_kind(ref)
  @types[ref]&.fetch('kind', nil)
end

#type_prefixes(domain)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 808

def type_prefixes(domain)
  return [domain] unless vendor_domain?(domain)

  messages = commands_for(domain) + events_for(domain)
  refs = messages.flat_map { |m| [m.dig('params', 'ref'), m.dig('result', 'ref')] }
  refs.compact.select { |ref| ref.include?('.') }.map { |ref| ref.split('.', 2).first }.uniq
end

#types_for(domain)

Structured value classes (records + discriminated unions) declared under "." Empty records are projector artifacts with nothing to carry, so they stay opaque hashes; only non-empty records and unions become classes. Command/event message envelopes (the {method, params} wire wrapper) are skipped — Transport forms that envelope, so nothing references them.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 827

def types_for(domain)
  owned_types(domain).filter_map do |name, type|
    case type['kind']
    when 'record' then record_class(name, type) unless type['fields'].empty? || suppressed_record?(type)
    when 'union' then union_class(name)
    when 'alias' then union_class(name) if type['type'].key?('union')
    end
  end
end

#union_alias_path(name) (private)

Absolute RBS path of a union's value alias: its class path with the last segment snake-cased (WebExtension::ExtensionData -> ...::WebExtension::extension_data), matching the type alias emitted alongside the class.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1000

def union_alias_path(name)
  segments = ruby_path(name).split('::')
  segments[-1] = BiDiGenerate.camel_to_snake(segments[-1])
  rbs_abs(segments.join('::'))
end

#union_class(name) (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1111

def union_class(name)
  type = @types[name]
  # A first-class union carries the schema's authoritative dispatch `selector`
  # (derived spec-faithfully, including null discriminators and the spec's choice
  # order); consume it rather than re-deriving and silently depending on emit
  # order. An alias-to-union (only input.Origin) has no selector — its const-string
  # arms aren't first-class types — so it keeps the structural re-derivation.
  klass = type['kind'] == 'union' ? union_from_selector(name, type['selector']) : union_from_alias(name)
  # A non-object_only union has a bare-scalar arm; only const-literal arms (scalar_values) are
  # modeled, so the runtime can validate an outbound scalar. A non-object_only union without them
  # is a shape the generator doesn't yet handle — fail here, at generation, not at a caller's runtime.
  if !klass.object_only && !klass.scalar_values?
    raise "non-object_only union #{name} has no scalar_values to validate its bare-scalar arm"
  end

  klass
end

#union_from_alias(name) (private)

The sole alias-union is input.Origin ("viewport" | "pointer" | ElementOrigin): a scalar-or-object union the object-payload selector model doesn't cover, so the projector leaves it an alias with no selector. Its object arm(s) carry a const discriminator; the bare-string arms need no dispatch (Union.from_json returns a non-Hash payload unchanged). So dispatch the ref arms by their const tag.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1174

def union_from_alias(name)
  spec = @types[name]
  consts = spec['type']['union'].filter_map { |arm| arm['ref'] }.to_h do |ref|
    const = @types[ref]['fields'].find { |f| f['type'].key?('const') }
    const || raise("alias-union #{name} arm #{ref} has no const discriminator to dispatch on")
    [ref, const]
  end
  variants = consts.map do |ref, const|
    VariantIR.new(mode: :value, value: const['type']['const'], ref: ruby_path(ref), requires: nil)
  end
  # An alias-union carries bare-scalar arms (input.Origin's "viewport"/"pointer"), so it
  # is never object_only — those arms must still pass a non-Hash payload through, but only
  # a value the schema pins in scalarValues (so a stray "banana" is still rejected outbound).
  UnionClass.new(ruby_name: BiDiGenerate.type_class_name(name),
                 discriminator_wire: consts.values.first['wire'], variants: variants, schema_name: name,
                 spec_href: spec['specHref'], object_only: spec['objectOnly'] ? true : false,
                 scalar_values: spec['type']['scalarValues'], **directionality(name))
end

#union_from_selector(name, selector) (private)

Map a union selector to dispatch variants the template renders:

{ by, variants, default? } -> a discriminator table (value => ref), {default}
as the fallback (it may itself be a union, which finishes the dispatch).
{ ordered: [{ ref, requires }] } -> presence rules in the spec's choice order.
{ correlated: true } -> resolved by request id, not the payload, so no payload
dispatch. Unreachable here: every correlated union is a top-level result
grouping, never domain-scoped, so it is never emitted as a class.
[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1136

def union_from_selector(name, selector)
  # A correlated union is resolved by request id, never the payload, so it carries
  # no dispatch — it must never be emitted (every one is a top-level result
  # grouping). Fail loudly if a future schema makes one domain-scoped rather than
  # emit a Union whose every parse would raise.
  if selector['correlated']
    raise "correlated union #{name} must not be emitted (resolved by request id, not payload)"
  end

  variants = selector['by'] ? discriminated_variants(selector) : ordered_variants(selector)
  raise "union #{name} selector yielded no dispatch variants" if variants.empty?

  UnionClass.new(ruby_name: BiDiGenerate.type_class_name(name),
                 discriminator_wire: selector['by'], variants: variants, schema_name: name,
                 spec_href: @types[name]['specHref'], object_only: @types[name]['objectOnly'] ? true : false,
                 **directionality(name))
end

#union_params(type, ref = nil) (private)

Merge a union's record variants into one flat param list for the command signature. A field is only required when every variant declares it required; variant-specific fields become optional. The command body dispatches these kwargs to the matching variant via Union.build, whose typed as_json handles null-vs-absent — so no nullable allowlist is needed.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1259

def union_params(type, ref = nil)
  variants = type['variants'].map { |variant_ref| @types[variant_ref] }
  return nil unless variants.all? { |v| v && v['kind'] == 'record' }

  selector = type['selector']
  guard_union_dispatch_keys_simple!(selector, ref)
  params = merged_params(variants.map { |v| v['fields'] }, type['variants'].any? { |v| extensible_record?(v) })
  annotate_discriminator_enum!(params, selector)
  params
end

#union_ref?(name) ⇒ Boolean (private)

True when a ref (following aliases) is a union — the only arm whose from_json tolerates a scalar sibling. A record arm would raise on one, so it is not carried.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 954

def union_ref?(name)
  type = @types[name]
  return false unless type
  return union_ref?(type['type']['ref']) if type['kind'] == 'alias' && type['type'].key?('ref')

  type['kind'] == 'union'
end

#vendor_added_commands(domain, namespace)

The commands a vendor defines in a spec domain (webExtension.moz:listExtensions). They are emitted on that domain's vendor variant, never on the shared class (see vendor_modules_for).

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 602

def vendor_added_commands(domain, namespace)
  return [] if vendor_domain?(domain)

  (@vendor.dig(namespace, 'commands') || []).select { |c| c['domain'] == domain }
end

#vendor_domain?(domain) ⇒ Boolean

A vendor module's domain keeps its wire prefix (moz:debugging); a spec domain has none.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 584

def vendor_domain?(domain) = domain.include?(':')

#vendor_events

A vendor event in a spec domain would need the same vendor-variant routing as a command; none exists yet, so fail generation rather than leak it into the shared class.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 592

def vendor_events
  events = vendor_messages('events')
  stray = events.reject { |e| vendor_domain?(e['domain']) }
  raise "vendor event #{stray.first['method']} in spec domain #{stray.first['domain']} is unsupported" if stray.any?

  events
end

#vendor_extended?(name) ⇒ Boolean (private)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 1048

def vendor_extended?(name)
  @vendor.values.any? { |spec| (spec['extends'] || {}).key?(name) }
end

#vendor_messages(kind)

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 586

def vendor_messages(kind)
  @vendor.values.flat_map { |section| section[kind] || [] }
end

#vendor_modules_for(domain)

The vendor modules a domain carries, one per namespace (moz → module Moz). The schema's vendor section names, per namespace, which shared type each vendor extends; we map that type back to the command that sends it, so the vendor method mirrors the base command's wire method and result while adding the typed vendor fields. Empty for any domain (or schema) with no vendor extensions, so non-vendor output is unaffected.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 681

def vendor_modules_for(domain)
  parent = BiDiGenerate.snake_to_class_name(BiDiGenerate.camel_to_snake(domain))
  @vendor.filter_map do |namespace, spec|
    overrides = (spec['extends'] || {}).filter_map do |type_name, entry|
      cmd = @commands.find { |c| c.dig('params', 'ref') == type_name }
      build_vendor_command(cmd, type_name, entry, namespace) if cmd && cmd['domain'] == domain
    end
    added = vendor_added_commands(domain, namespace).map { |cmd| build_added_command(cmd, namespace, domain) }
    commands = overrides + added
    next if commands.empty?

    VendorModule.new(name: BiDiGenerate.snake_to_class_name(namespace), namespace: namespace, parent: parent,
                     commands: commands)
  end
end

#vendor_param(field, namespace, taken)

A vendor field's ruby name drops its namespace prefix (moz:permanent → permanent): the module already scopes it, so re-encoding the namespace in every identifier is redundant. The wire key is untouched. Falls back to the prefixed name only if stripping would collide with a shared param on the same command.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 729

def vendor_param(field, namespace, taken)
  stripped = field['name'].sub(/\A#{Regexp.escape(namespace)}:/, '')
  ruby_name = BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(stripped))
  ruby_name = BiDiGenerate.safe_field_name(BiDiGenerate.camel_to_snake(field['name'])) if taken.include?(ruby_name)
  Param.new(
    ruby_name: ruby_name,
    wire_name: field['wire'],
    required: field['required'],
    enum: enum_const(field['type']),
    rbs: rbs_type(field['type'])
  )
end

#vendor_types_reached(domain)

The vendor types a vendor-added command in this spec domain reaches through its params and result, so they are emitted in the domain's module beside the command that returns them.

[ GitHub ]

  
# File 'rb/lib/selenium/webdriver/bidi/support/bidi_generate.rb', line 794

def vendor_types_reached(domain)
  commands = @vendor.keys.flat_map { |namespace| vendor_added_commands(domain, namespace) }
  pending = commands.flat_map { |c| [c.dig('params', 'ref'), c.dig('result', 'ref')] }.compact
  reached = Set.new
  until pending.empty?
    name = pending.pop
    next if reached.include?(name) || !@vendor_types.key?(name)

    reached << name
    pending.concat(plain_refs(@types[name]) + (@types[name]['variants'] || []))
  end
  reached
end