@@ -138,8 +138,13 @@ const DOMAIN_CLASSES = {
138138function resolveInputPath ( p ) {
139139 if ( ! p ) return null
140140 if ( ! process . env . BAZEL_BINDIR ) return resolve ( p )
141- const prefix = process . env . BAZEL_BINDIR + '/'
142- return resolve ( p . startsWith ( prefix ) ? p . slice ( prefix . length ) : p )
141+ // Normalize both strings to forward slashes before prefix-stripping so that
142+ // mixed separators on Windows (BAZEL_BINDIR uses '\', $(location) uses '/')
143+ // do not cause the startsWith check to silently fail.
144+ const normalizedP = p . replaceAll ( '\\' , '/' )
145+ const normalizedBindir = process . env . BAZEL_BINDIR . replaceAll ( '\\' , '/' )
146+ const prefix = normalizedBindir + '/'
147+ return resolve ( normalizedP . startsWith ( prefix ) ? normalizedP . slice ( prefix . length ) : normalizedP )
143148}
144149
145150// ============================================================
@@ -169,6 +174,10 @@ async function main() {
169174 // The --output-dir arg is passed as <pkg>/<rel_path>, which is already
170175 // relative to BAZEL_BINDIR (= CWD), so it needs no special handling.
171176 const cddlPath = resolveInputPath ( args . cddl )
177+ if ( ! existsSync ( cddlPath ) ) {
178+ console . error ( `Error: CDDL file not found: ${ cddlPath } ` )
179+ process . exit ( 1 )
180+ }
172181 const outputDir = resolve ( args [ 'output-dir' ] )
173182 const specVersion = args [ 'spec-version' ]
174183
@@ -241,7 +250,16 @@ function loadEnhancements(manifestPath) {
241250 console . warn ( `Warning: enhancements manifest not found: ${ fullPath } ` )
242251 return { }
243252 }
244- return JSON . parse ( readFileSync ( fullPath , 'utf8' ) )
253+ let parsed
254+ try {
255+ parsed = JSON . parse ( readFileSync ( fullPath , 'utf8' ) )
256+ } catch ( err ) {
257+ throw new Error ( `Failed to parse enhancements manifest at ${ fullPath } : ${ err . message } ` )
258+ }
259+ if ( typeof parsed !== 'object' || parsed === null || Array . isArray ( parsed ) ) {
260+ throw new Error ( `Enhancements manifest at ${ fullPath } must be a JSON object, got ${ Array . isArray ( parsed ) ? 'array' : typeof parsed } ` )
261+ }
262+ return parsed
245263}
246264
247265// ============================================================
@@ -752,8 +770,6 @@ function generateDomainFile({
752770 parts . push ( ` send(command: Record<string, unknown>): Promise<unknown>` )
753771 parts . push ( ` subscribe(event: string | string[], contexts?: string[]): Promise<void>` )
754772 parts . push ( ` on(event: string, listener: (params: unknown) => void): void` )
755- parts . push ( ` /** Raw WebSocket — used by event listeners to attach message handlers. */` )
756- parts . push ( ` readonly socket: { on(event: 'message', listener: (data: { toString(): string }) => void): void }` )
757773 parts . push ( `}` )
758774 parts . push ( '' )
759775 }
@@ -826,6 +842,10 @@ function generateClass({ className, commands, events, enhancement, emptyResultTy
826842 lines . push ( ` private constructor(private readonly bidi: BidiConnection) {}` )
827843 lines . push ( '' )
828844 lines . push ( ` static async create(driver: unknown): Promise<${ className } > {` )
845+ lines . push ( ` const caps = await (driver as { getCapabilities(): Promise<{ get(key: string): unknown }> }).getCapabilities()` )
846+ lines . push ( ` if (!caps.get('webSocketUrl')) {` )
847+ lines . push ( ` throw new Error('WebDriver instance must support BiDi protocol')` )
848+ lines . push ( ` }` )
829849 lines . push ( ` const bidi = await (driver as { getBidi(): Promise<BidiConnection> }).getBidi()` )
830850 lines . push ( ` return new ${ className } (bidi)` )
831851 lines . push ( ` }` )
@@ -901,19 +921,13 @@ function generateEventMethod(evt) {
901921 const lines = [ ]
902922 lines . push ( ` async ${ onMethodName } (callback: ${ cbType } ): Promise<void> {` )
903923 lines . push ( ` await this.bidi.subscribe('${ methodStr } ')` )
904- // Use the raw WebSocket message listener — the same pattern as the hand-coded
905- // logInspector.js / browsingContext.js classes. bidi/index.js intentionally
906- // ignores WebSocket messages without a numeric "id" in its response dispatcher,
907- // so event payloads must be captured directly from the socket instead.
908- lines . push ( ` const ws = this.bidi.socket` )
909- lines . push ( ` ws.on('message', (data: { toString(): string }) => {` )
910- lines . push ( ` const msg = JSON.parse(data.toString()) as {` )
911- lines . push ( ` method?: string` )
912- lines . push ( ` params?: unknown` )
913- lines . push ( ` }` )
914- lines . push ( ` if (msg.method === '${ methodStr } ') {` )
915- lines . push ( ` callback(${ paramsTypeName ? `msg.params as ${ paramsTypeName } ` : 'msg.params' } )` )
916- lines . push ( ` }` )
924+ // bidi/index.js emits BiDi events by method name through its single shared
925+ // message dispatcher (which already handles JSON parsing and closed-state
926+ // guards). Using bidi.on() here avoids attaching a new ws.on('message', ...)
927+ // listener on every subscription call, preventing listener accumulation and
928+ // MaxListeners warnings.
929+ lines . push ( ` this.bidi.on('${ methodStr } ', (params: unknown) => {` )
930+ lines . push ( ` callback(${ paramsTypeName ? `params as ${ paramsTypeName } ` : 'params' } )` )
917931 lines . push ( ` })` )
918932 lines . push ( ` }` )
919933 return lines . join ( '\n' )
0 commit comments