Skip to content

Conversation

@james-tindal
Copy link

@james-tindal james-tindal commented Nov 25, 2025

Proposed changes

Please don't make me import enums or guess the types

Eslint rule

This ignores eslint rule @typescript-eslint/no-unsafe-declaration-merging
Since it only changes the type of a method that is already implemented, it's not a case of unsafe declaration merging.

Types of changes

What types of changes does your code introduce to the community JavaScript SDK?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update or tests (if none of the other choices apply)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I have read the CONTRIBUTING doc
  • I have lint'ed all of my code using repo standards
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features
    • Strongly-typed event listener interface for live transcription: typed handlers for transcript, metadata, utterance end, speech started, error, connection state, and unhandled events.
    • Error handlers now receive a standardized API error object.
    • Open/connect handler now receives the live client instance for richer callbacks.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 25, 2025

Walkthrough

Adds a public ListenLiveClient interface with eight strongly-typed on() overloads, updates imports to include new event and error types, and keeps the existing export alias; includes a small eslint-disable-line on the class declaration.

Changes

Cohort / File(s) Change Summary
ListenLive client type additions
src/packages/ListenLiveClient.ts
Adds a public ListenLiveClient interface that declares eight overloaded on() methods with specific listener signatures for Transcript, Metadata, UtteranceEnd, SpeechStarted, Error, Close, Open (now receives the ListenLiveClient instance), and Unhandled. Updates imports to include LiveTranscriptionEvent, LiveMetadataEvent, UtteranceEndEvent, SpeechStartedEvent, and replaces DeepgramError with DeepgramApiError. Adds an eslint-disable-line directive to accommodate declaration merging and preserves the existing LiveClient export alias.
Package metadata
package.json
Minor manifest included in the diff (no functional changes described).

Sequence Diagram(s)

(omitted — changes are type/interface declarations only; control flow unchanged)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify the on() overload signatures match runtime event emission points.
  • Confirm imported event and error types are exported from their source modules.
  • Check the Open listener change (receiving ListenLiveClient) doesn't break existing consumers.
  • Ensure the eslint directive is scoped and justified.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is incomplete and vague, using non-descriptive language that fails to convey the specific change. While it hints at enums removal, it lacks clarity and contains trailing words. Revise the title to be complete and specific, such as 'Add strongly-typed event listener interface for ListenLiveClient' or 'Replace enum usage with template literal type overloads in ListenLiveClient'.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3cbb3e and a74c24a.

📒 Files selected for processing (1)
  • src/packages/ListenLiveClient.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/packages/ListenLiveClient.ts (6)
src/packages/AbstractLiveClient.ts (1)
  • AbstractLiveClient (542-542)
src/lib/types/LiveTranscriptionEvent.ts (1)
  • LiveTranscriptionEvent (1-34)
src/lib/types/LiveMetadataEvent.ts (1)
  • LiveMetadataEvent (1-9)
src/lib/types/UtteranceEndEvent.ts (1)
  • UtteranceEndEvent (1-5)
src/lib/types/SpeechStartedEvent.ts (1)
  • SpeechStartedEvent (1-5)
src/lib/errors.ts (1)
  • DeepgramApiError (14-30)
🪛 Biome (2.1.2)
src/packages/ListenLiveClient.ts

[error] 21-21: This class is unsafely merged with an interface.

The interface is declared here.

The TypeScript compiler doesn't check whether properties defined in the interface are initialized in the class.

(lint/suspicious/noUnsafeDeclarationMerging)

🔇 Additional comments (6)
src/packages/ListenLiveClient.ts (6)

3-4: LGTM!

The import additions are well-aligned with the strongly-typed interface overloads below. All imported event types are properly utilized.


21-21: LGTM!

The eslint-disable directive is appropriate for the declaration merging pattern used here. This allows strongly-typed event listeners without modifying the base class implementation.


150-150: LGTM!

The eslint-disable directive is correctly placed for the interface declaration merging.


152-155: LGTM!

These event listener overloads correctly match their corresponding implementations in setupConnection and provide strong typing for transcript, metadata, utterance end, speech started, and unhandled events.

Also applies to: 159-159


157-157: LGTM!

The Close event correctly specifies no parameters, which aligns with typical WebSocket close event behavior.


158-158: The Open event correctly receives the client instance parameter.

Verification confirms that AbstractLiveClient.setupConnectionEvents() at line 502 emits the Open event with this (the client instance) as the parameter: this.emit(events.Open, this). This matches the type signature in ListenLiveClient line 158 which expects listener: (client: ListenLiveClient) => void. The implementation is correct.

on(eventName: `${LiveTranscriptionEvents.Metadata}`, listener: (data: LiveMetadataEvent) => void): this
on(eventName: `${LiveTranscriptionEvents.UtteranceEnd}`, listener: (data: UtteranceEndEvent) => void): this
on(eventName: `${LiveTranscriptionEvents.SpeechStarted}`, listener: (data: SpeechStartedEvent) => void): this
on(eventName: `${LiveTranscriptionEvents.Error}`, listener: (data: DeepgramApiError) => void): this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Script executed:

#!/bin/bash
# First, find the ListenLiveClient.ts file and examine the Error event listener and implementation

# Find the file
find . -name "ListenLiveClient.ts" -type f

# Read the file to check lines around 79-88 and 156
if [ -f "src/packages/ListenLiveClient.ts" ]; then
  echo "=== Lines 70-95 (error emission) ==="
  sed -n '70,95p' src/packages/ListenLiveClient.ts
  echo ""
  echo "=== Lines 150-165 (Error event listener) ==="
  sed -n '150,165p' src/packages/ListenLiveClient.ts
fi

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

./src/packages/ListenLiveClient.ts
=== Lines 70-95 (error emission) ===
            this.emit(LiveTranscriptionEvents.Transcript, data);
          } else if (data.type === LiveTranscriptionEvents.UtteranceEnd) {
            this.emit(LiveTranscriptionEvents.UtteranceEnd, data);
          } else if (data.type === LiveTranscriptionEvents.SpeechStarted) {
            this.emit(LiveTranscriptionEvents.SpeechStarted, data);
          } else {
            this.emit(LiveTranscriptionEvents.Unhandled, data);
          }
        } catch (error) {
          this.emit(LiveTranscriptionEvents.Error, {
            event,
            message: "Unable to parse `data` as JSON.",
            error,
            url: this.conn?.url,
            readyState: this.conn?.readyState,
            data:
              event.data?.toString().substring(0, 200) +
              (event.data?.toString().length > 200 ? "..." : ""),
          });
        }
      };
    }
  }

  /**
   * Sends additional config to the connected session.

=== Lines 150-165 (Error event listener) ===
export { ListenLiveClient as LiveClient };

</shell_result>


Script executed:

#!/bin/bash
# Search for the on method signature and Error event listener

echo "=== Searching for Error event listener in ListenLiveClient ==="
rg -n "on.*Error" src/packages/ListenLiveClient.ts -A 1 -B 1

echo ""
echo "=== Looking for the on method overloads ==="
rg -n "^\s*on\(" src/packages/ListenLiveClient.ts -A 1

echo ""
echo "=== Finding AbstractLiveClient ==="
find . -name "AbstractLiveClient.ts" -type f

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Searching for Error event listener in ListenLiveClient ===
49-   * - When the connection is closed, it emits the `LiveTranscriptionEvents.Close` event.
50:   * - When an error occurs on the connection, it emits the `LiveTranscriptionEvents.Error` event.
51-   * - When a message is received, it parses the message and emits the appropriate event based on the message type, such as `LiveTranscriptionEvents.Metadata`, `LiveTranscriptionEvents.Transcript`, `LiveTranscriptionEvents.UtteranceEnd`, and `LiveTranscriptionEvents.SpeechStarted`.
--
57-      Close: LiveTranscriptionEvents.Close,
58:      Error: LiveTranscriptionEvents.Error,
59-    });
--
78-        } catch (error) {
79:          this.emit(LiveTranscriptionEvents.Error, {
80-            event,

=== Looking for the on method overloads ===

=== Finding AbstractLiveClient ===
./src/packages/AbstractLiveClient.ts

</shell_result>


Script executed:

#!/bin/bash
# Check AbstractLiveClient for on method signatures and setupConnectionEvents

echo "=== File size check ==="
wc -l ./src/packages/AbstractLiveClient.ts

echo ""
echo "=== Search for on method overloads with Error event ==="
rg -n "on.*Error" ./src/packages/AbstractLiveClient.ts -A 2 -B 2

echo ""
echo "=== Search for setupConnectionEvents ==="
rg -n "setupConnectionEvents" ./src/packages/AbstractLiveClient.ts -A 15

echo ""
echo "=== Search for all on method signatures ==="
rg -n "^\s*on\(" ./src/packages/AbstractLiveClient.ts -A 1 | head -50

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== File size check ===
542 ./src/packages/AbstractLiveClient.ts

=== Search for on method overloads with Error event ===
294-   * ```typescript
295-   * // Enhanced error information is now available in error events:
296:   * connection.on(LiveTranscriptionEvents.Error, (err) => {
297:   *   console.error("WebSocket Error:", err.message);
298-   *
299-   *   // Access HTTP status code (e.g., 502, 403, etc.)
--
324-   *   // Access the enhanced error object for detailed debugging
325-   *   if (err.error?.name === 'DeepgramWebSocketError') {
326:   *     console.error("Enhanced Error Details:", err.error.toJSON());
327-   *   }
328-   * });
--
414-  ) {
415-    // Create the enhanced error for detailed debugging
416:    const enhancedError = new DeepgramWebSocketError(
417-      (event as ErrorEvent).message || "WebSocket connection error",
418-      {
--
497-   * @protected
498-   */
499:  protected setupConnectionEvents(events: { Open: string; Close: string; Error: string }): void {
500-    if (this.conn) {
501-      this.conn.onopen = () => {
--
507-      };
508-
509:      this.conn.onerror = (event: ErrorEvent) => {
510:        const enhancedInfo = this.extractErrorInformation(event, this.conn || undefined);
511:        const enhancedError = this.createEnhancedError(event, enhancedInfo);
512-        this.emit(events.Error, enhancedError);
513-      };

=== Search for setupConnectionEvents ===
499:  protected setupConnectionEvents(events: { Open: string; Close: string; Error: string }): void {
500-    if (this.conn) {
501-      this.conn.onopen = () => {
502-        this.emit(events.Open, this);
503-      };
504-
505-      this.conn.onclose = (event: any) => {
506-        this.emit(events.Close, event);
507-      };
508-
509-      this.conn.onerror = (event: ErrorEvent) => {
510-        const enhancedInfo = this.extractErrorInformation(event, this.conn || undefined);
511-        const enhancedError = this.createEnhancedError(event, enhancedInfo);
512-        this.emit(events.Error, enhancedError);
513-      };
514-    }

=== Search for all on method signatures ===

</shell_result>


Script executed:

#!/bin/bash
# Search for on method signatures and error types

echo "=== Search for on method definitions (broader search) ==="
rg -n "^\s*on\(" ./src/packages/AbstractLiveClient.ts -A 1 | head -100

echo ""
echo "=== Search for DeepgramApiError type ==="
rg -n "DeepgramApiError" ./src/packages/AbstractLiveClient.ts -B 2 -A 2

echo ""
echo "=== Search for createEnhancedError method ==="
rg -n "createEnhancedError" ./src/packages/AbstractLiveClient.ts -A 10 | head -30

echo ""
echo "=== Check what DeepgramWebSocketError is ==="
rg -n "class DeepgramWebSocketError" . -A 5

echo ""
echo "=== Look for on method with Error in ListenLiveClient (check full file structure) ==="
head -200 ./src/packages/ListenLiveClient.ts | tail -100

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Search for on method definitions (broader search) ===

=== Search for DeepgramApiError type ===

=== Search for createEnhancedError method ===
405:  protected createEnhancedError(
406-    event: ErrorEvent | Event,
407-    enhancedInfo: {
408-      statusCode?: number;
409-      requestId?: string;
410-      responseHeaders?: Record<string, string>;
411-      url?: string;
412-      readyState?: number;
413-    }
414-  ) {
415-    // Create the enhanced error for detailed debugging
--
511:        const enhancedError = this.createEnhancedError(event, enhancedInfo);
512-        this.emit(events.Error, enhancedError);
513-      };
514-    }
515-  }
516-
517-  /**
518-   * Sets up the connection event handlers.
519-   *
520-   * @abstract Requires subclasses to set up context aware event handlers.
521-   */

=== Check what DeepgramWebSocketError is ===
./src/lib/errors.ts:56:export class DeepgramWebSocketError extends DeepgramError {
./src/lib/errors.ts-57-  originalEvent?: ErrorEvent | Event;
./src/lib/errors.ts-58-  statusCode?: number;
./src/lib/errors.ts-59-  requestId?: string;
./src/lib/errors.ts-60-  responseHeaders?: Record<string, string>;
./src/lib/errors.ts-61-  url?: string;

=== Look for on method with Error in ListenLiveClient (check full file structure) ===
   * - When a message is received, it parses the message and emits the appropriate event based on the message type, such as `LiveTranscriptionEvents.Metadata`, `LiveTranscriptionEvents.Transcript`, `LiveTranscriptionEvents.UtteranceEnd`, and `LiveTranscriptionEvents.SpeechStarted`.
   */
  public setupConnection(): void {
    // Set up standard connection events (open, close, error) using abstracted method
    this.setupConnectionEvents({
      Open: LiveTranscriptionEvents.Open,
      Close: LiveTranscriptionEvents.Close,
      Error: LiveTranscriptionEvents.Error,
    });

    // Set up message handling specific to transcription
    if (this.conn) {
      this.conn.onmessage = (event: MessageEvent) => {
        try {
          const data: any = JSON.parse(event.data.toString());

          if (data.type === LiveTranscriptionEvents.Metadata) {
            this.emit(LiveTranscriptionEvents.Metadata, data);
          } else if (data.type === LiveTranscriptionEvents.Transcript) {
            this.emit(LiveTranscriptionEvents.Transcript, data);
          } else if (data.type === LiveTranscriptionEvents.UtteranceEnd) {
            this.emit(LiveTranscriptionEvents.UtteranceEnd, data);
          } else if (data.type === LiveTranscriptionEvents.SpeechStarted) {
            this.emit(LiveTranscriptionEvents.SpeechStarted, data);
          } else {
            this.emit(LiveTranscriptionEvents.Unhandled, data);
          }
        } catch (error) {
          this.emit(LiveTranscriptionEvents.Error, {
            event,
            message: "Unable to parse `data` as JSON.",
            error,
            url: this.conn?.url,
            readyState: this.conn?.readyState,
            data:
              event.data?.toString().substring(0, 200) +
              (event.data?.toString().length > 200 ? "..." : ""),
          });
        }
      };
    }
  }

  /**
   * Sends additional config to the connected session.
   *
   * @param config - The configuration options to apply to the LiveClient.
   * @param config.numerals - We currently only support numerals.
   */
  public configure(config: LiveConfigOptions): void {
    this.send(
      JSON.stringify({
        type: "Configure",
        processors: config,
      })
    );
  }

  /**
   * Sends a "KeepAlive" message to the server to maintain the connection.
   */
  public keepAlive(): void {
    this.send(
      JSON.stringify({
        type: "KeepAlive",
      })
    );
  }

  /**
   * Sends a "Finalize" message to flush any transcription sitting in the server's buffer.
   */
  public finalize(): void {
    this.send(
      JSON.stringify({
        type: "Finalize",
      })
    );
  }

  /**
   * @deprecated Since version 3.4. Will be removed in version 4.0. Use `requestClose` instead.
   */
  public finish(): void {
    this.requestClose();
  }

  /**
   * Requests the server close the connection.
   */
  public requestClose(): void {
    this.send(
      JSON.stringify({
        type: "CloseStream",
      })
    );
  }
}

export { ListenLiveClient as LiveClient };

</shell_result>


Script executed:

#!/bin/bash
# Check the class hierarchy and EventEmitter implementation

echo "=== Check ListenLiveClient imports and class definition ==="
head -30 ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Check AbstractLiveClient imports and class definition ==="
head -40 ./src/packages/AbstractLiveClient.ts

echo ""
echo "=== Search for on method with full context in AbstractLiveClient ==="
rg -n "on\s*\(" ./src/packages/AbstractLiveClient.ts | head -20

echo ""
echo "=== Look for EventEmitter or similar pattern ==="
rg -n "EventEmitter\|on\s*\(.*listener.*\)" ./src/packages/ -A 1 | head -40

echo ""
echo "=== Check if there's a types file with event listener definitions ==="
find ./src -name "*.ts" -type f | xargs grep -l "LiveTranscriptionEvents.*Error" | head -5

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Check ListenLiveClient imports and class definition ===
import { AbstractLiveClient } from "./AbstractLiveClient";
import { LiveTranscriptionEvents } from "../lib/enums";
import type { LiveSchema, LiveConfigOptions, DeepgramClientOptions } from "../lib/types";
import { DeepgramError } from "../lib/errors";

/**
 * The `ListenLiveClient` class extends the `AbstractLiveClient` class and provides functionality for setting up and managing a WebSocket connection for live transcription.
 *
 * The constructor takes in `DeepgramClientOptions` and an optional `LiveSchema` object, as well as an optional `endpoint` string. It then calls the `connect` method of the parent `AbstractLiveClient` class to establish the WebSocket connection.
 *
 * The `setupConnection` method is responsible for handling the various events that can occur on the WebSocket connection, such as opening, closing, and receiving messages. It sets up event handlers for these events and emits the appropriate events based on the message type.
 *
 * The `configure` method allows you to send additional configuration options to the connected session, such as enabling numerals.
 *
 * The `keepAlive` method sends a "KeepAlive" message to the server to maintain the connection.
 *
 * The `requestClose` method requests the server to close the connection.
 *
 * The `finish` method is deprecated as of version 3.4 and will be removed in version 4.0. Use `requestClose` instead.
 */
export class ListenLiveClient extends AbstractLiveClient {
  public namespace: string = "listen";

  /**
   * Constructs a new `ListenLiveClient` instance with the provided options.
   *
   * @param options - The `DeepgramClientOptions` to use for the client connection.
   * @param transcriptionOptions - An optional `LiveSchema` object containing additional configuration options for the live transcription.
   * @param endpoint - An optional string representing the WebSocket endpoint to connect to. Defaults to `:version/listen`.
   */

=== Check AbstractLiveClient imports and class definition ===
import { AbstractClient, noop } from "./AbstractClient";
import { CONNECTION_STATE, SOCKET_STATES } from "../lib/constants";
import type { DeepgramClientOptions, LiveSchema } from "../lib/types";
import type { WebSocket as WSWebSocket } from "ws";
import { isBun } from "../lib/runtime";
import { DeepgramWebSocketError } from "../lib/errors";

/**
 * Represents a constructor for a WebSocket-like object that can be used in the application.
 * The constructor takes the following parameters:
 * @param address - The URL or address of the WebSocket server.
 * @param _ignored - An optional parameter that is ignored.
 * @param options - An optional object containing headers to be included in the WebSocket connection.
 * @returns A WebSocket-like object that implements the WebSocketLike interface.
 */
interface WebSocketLikeConstructor {
  new(
    address: string | URL,
    _ignored?: any,
    options?: { headers: object | undefined }
  ): WebSocketLike;
}

/**
 * Represents the types of WebSocket-like connections that can be used in the application.
 * This type is used to provide a common interface for different WebSocket implementations,
 * such as the native WebSocket API, a WebSocket wrapper library, or a dummy implementation
 * for testing purposes.
 */
type WebSocketLike = WebSocket | WSWebSocket | WSWebSocketDummy;

/**
 * Represents the types of data that can be sent or received over a WebSocket-like connection.
 */
type SocketDataLike = string | ArrayBufferLike | Blob;

/**
 * Represents an error that occurred in a WebSocket-like connection.
 * @property {any} error - The underlying error object.
 * @property {string} message - A human-readable error message.

=== Search for on method with full context in AbstractLiveClient ===
131:      this.setupConnection();
147:        this.setupConnection();
160:      this.setupConnection();
180:      this.setupConnection();
199:      this.conn.onclose = function () { }; // noop
296:   * connection.on(LiveTranscriptionEvents.Error, (err) => {
335:  protected extractErrorInformation(
510:        const enhancedInfo = this.extractErrorInformation(event, this.conn || undefined);
522:  abstract setupConnection(): void;

=== Look for EventEmitter or similar pattern ===

=== Check if there's a types file with event listener definitions ===
./src/packages/AbstractLiveClient.ts
./src/packages/ListenLiveClient.ts

</shell_result>


Script executed:

#!/bin/bash
# Check AbstractClient for on method

echo "=== Check AbstractClient ==="
find ./src -name "AbstractClient.ts" -type f

echo ""
echo "=== Read AbstractClient to find on method ==="
if [ -f "./src/packages/AbstractClient.ts" ]; then
  wc -l ./src/packages/AbstractClient.ts
  echo ""
  echo "=== Search for on method in AbstractClient ==="
  rg -n "on\s*\(" ./src/packages/AbstractClient.ts -B 2 -A 5 | head -100
fi

echo ""
echo "=== Search for event listener type definitions ==="
rg -n "listener.*DeepgramApiError\|listener.*Error" ./src --type ts -B 1 -A 1 | head -50

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Check AbstractClient ===
./src/packages/AbstractClient.ts

=== Read AbstractClient to find on method ===
172 ./src/packages/AbstractClient.ts

=== Search for on method in AbstractClient ===
143-     * Version and template the endpoint for input argument..
144-     */
145:    endpoint = endpoint.replace(/:(\w+)/g, function (_, key) {
146-      return fields![key];
147-    });
148-
149-    /**
150-     * Create a URL object.

=== Search for event listener type definitions ===

</shell_result>


Script executed:

#!/bin/bash
# Read full AbstractClient and search for EventEmitter

echo "=== Read full AbstractClient.ts ==="
cat -n ./src/packages/AbstractClient.ts

echo ""
echo "=== Search for EventEmitter in project ==="
rg -n "EventEmitter\|extends.*EventEmitter" ./src --type ts

echo ""
echo "=== Search for on method definition anywhere ==="
rg -n "^\s*(public|protected)?\s*on\s*\(" ./src --type ts -A 2 | head -50

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Read full AbstractClient.ts ===
     1	import { EventEmitter } from "events";
     2	import { DEFAULT_OPTIONS, DEFAULT_URL } from "../lib/constants";
     3	import { DeepgramError } from "../lib/errors";
     4	import { appendSearchParams, applyDefaults, convertLegacyOptions } from "../lib/helpers";
     5	import type {
     6	  DeepgramClientOptions,
     7	  DefaultClientOptions,
     8	  DefaultNamespaceOptions,
     9	  NamespaceOptions,
    10	} from "../lib/types";
    11	
    12	export const noop = () => {};
    13	
    14	/**
    15	 * Represents an abstract Deepgram client that provides a base implementation for interacting with the Deepgram API.
    16	 *
    17	 * The `AbstractClient` class is responsible for:
    18	 * - Initializing the Deepgram API key
    19	 * - Applying default options for the client and namespace
    20	 * - Providing a namespace for organizing API requests
    21	 *
    22	 * Subclasses of `AbstractClient` should implement the specific functionality for interacting with the Deepgram API.
    23	 */
    24	export abstract class AbstractClient extends EventEmitter {
    25	  protected factory: Function | undefined = undefined;
    26	  protected key: string | undefined = undefined;
    27	  protected accessToken: string | undefined = undefined;
    28	  protected options: DefaultClientOptions;
    29	  public namespace: string = "global";
    30	  public version: string = "v1";
    31	  public baseUrl: string = DEFAULT_URL;
    32	  public logger: Function = noop;
    33	
    34	  /**
    35	   * Constructs a new instance of the DeepgramClient class with the provided options.
    36	   *
    37	   * @param options - The options to configure the DeepgramClient instance.
    38	   * @param options.key - The Deepgram API key to use for authentication. If not provided, the `DEEPGRAM_API_KEY` environment variable will be used.
    39	   * @param options.accessToken - The Deepgram access token to use for authentication. If not provided, the `DEEPGRAM_ACCESS_TOKEN` environment variable will be used.
    40	   * @param options.global - Global options that apply to all requests made by the DeepgramClient instance.
    41	   * @param options.global.fetch - Options to configure the fetch requests made by the DeepgramClient instance.
    42	   * @param options.global.fetch.options - Additional options to pass to the fetch function, such as `url` and `headers`.
    43	   * @param options.namespace - Options specific to a particular namespace within the DeepgramClient instance.
    44	   */
    45	  constructor(options: DeepgramClientOptions) {
    46	    super();
    47	
    48	    // run the factory for the access token if it's a function
    49	    if (typeof options.accessToken === "function") {
    50	      this.factory = options.accessToken;
    51	      this.accessToken = this.factory();
    52	    } else {
    53	      this.accessToken = options.accessToken;
    54	    }
    55	
    56	    // run the factory for the key if it's a function
    57	    if (typeof options.key === "function") {
    58	      this.factory = options.key;
    59	      this.key = this.factory();
    60	    } else {
    61	      this.key = options.key;
    62	    }
    63	
    64	    // implement priority-based credential resolution for environment variables
    65	    if (!this.key && !this.accessToken) {
    66	      // check for DEEPGRAM_ACCESS_TOKEN first (higher priority)
    67	      this.accessToken = process.env.DEEPGRAM_ACCESS_TOKEN as string;
    68	
    69	      // if still no access token, fall back to DEEPGRAM_API_KEY (lower priority)
    70	      if (!this.accessToken) {
    71	        this.key = process.env.DEEPGRAM_API_KEY as string;
    72	      }
    73	    }
    74	
    75	    // if we STILL have neither, throw an error
    76	    if (!this.key && !this.accessToken) {
    77	      throw new DeepgramError("A deepgram API key or access token is required.");
    78	    }
    79	
    80	    options = convertLegacyOptions(options);
    81	
    82	    /**
    83	     * Apply default options.
    84	     */
    85	    this.options = applyDefaults<DeepgramClientOptions, DefaultClientOptions>(
    86	      options,
    87	      DEFAULT_OPTIONS
    88	    );
    89	  }
    90	
    91	  /**
    92	   * Sets the version for the current instance of the Deepgram API and returns the instance.
    93	   *
    94	   * @param version - The version to set for the Deepgram API instance. Defaults to "v1" if not provided.
    95	   * @returns The current instance of the AbstractClient with the updated version.
    96	   */
    97	  public v(version: string = "v1"): this {
    98	    this.version = version;
    99	
   100	    return this;
   101	  }
   102	
   103	  /**
   104	   * Gets the namespace options for the current instance of the AbstractClient.
   105	   * The namespace options include the default options merged with the global options,
   106	   * and the API key for the current instance.
   107	   *
   108	   * @returns The namespace options for the current instance.
   109	   */
   110	  get namespaceOptions(): DefaultNamespaceOptions {
   111	    const defaults = applyDefaults<NamespaceOptions, DefaultNamespaceOptions>(
   112	      (this.options as any)[this.namespace],
   113	      this.options.global
   114	    );
   115	
   116	    return {
   117	      ...defaults,
   118	      key: this.key,
   119	    };
   120	  }
   121	
   122	  /**
   123	   * Generates a URL for an API endpoint with optional query parameters and transcription options.
   124	   *
   125	   * @param endpoint - The API endpoint URL, which may contain placeholders for fields.
   126	   * @param fields - An optional object containing key-value pairs to replace placeholders in the endpoint URL.
   127	   * @param transcriptionOptions - Optional transcription options to include as query parameters in the URL.
   128	   * @returns A URL object representing the constructed API request URL.
   129	   */
   130	  public getRequestUrl(
   131	    endpoint: string,
   132	    fields: { [key: string]: string } = { version: this.version },
   133	    transcriptionOptions?: {
   134	      [key: string]: unknown;
   135	    }
   136	  ): URL {
   137	    /**
   138	     * If we pass in fields without a version, set a version.
   139	     */
   140	    fields.version = this.version;
   141	
   142	    /**
   143	     * Version and template the endpoint for input argument..
   144	     */
   145	    endpoint = endpoint.replace(/:(\w+)/g, function (_, key) {
   146	      return fields![key];
   147	    });
   148	
   149	    /**
   150	     * Create a URL object.
   151	     */
   152	    const url = new URL(endpoint as string, this.baseUrl);
   153	
   154	    /**
   155	     * If there are transcription options, append them to the request as URL querystring parameters
   156	     */
   157	    if (transcriptionOptions) {
   158	      appendSearchParams(url.searchParams, transcriptionOptions);
   159	    }
   160	
   161	    return url;
   162	  }
   163	
   164	  /**
   165	   * Logs the message.
   166	   *
   167	   * For customized logging, `this.logger` can be overridden.
   168	   */
   169	  public log(kind: string, msg: string, data?: any) {
   170	    this.logger(kind, msg, data);
   171	  }
   172	}

=== Search for EventEmitter in project ===

=== Search for on method definition anywhere ===

</shell_result>


Script executed:

#!/bin/bash
# Search for TypeScript type overloads for on method

echo "=== Search for on method overloads in types files ==="
find ./src -name "*.ts" -type f -exec grep -l "on\s*\(" {} \; | xargs grep -l "eventName\|listener" | head -10

echo ""
echo "=== Search for type definitions with Error listener ==="
rg -n "listener.*Error\|Error.*listener" ./src/lib/types --type ts -B 2 -A 2

echo ""
echo "=== Look for type definitions file ==="
ls -la ./src/lib/types*

echo ""
echo "=== Search for LiveTranscriptionEvents enum ==="
rg -n "LiveTranscriptionEvents" ./src/lib/enums -A 20

echo ""
echo "=== Search for event listener type definitions ==="
rg -n "on\s*\(.*eventName" ./src --type ts -B 1 -A 3 | head -80

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Search for on method overloads in types files ===

=== Search for type definitions with Error listener ===

=== Look for type definitions file ===
total 188
drwxr-xr-x 2 jailuser jailuser  960 Nov 26 18:07 .
drwxr-xr-x 4 jailuser jailuser  200 Nov 26 18:07 ..
-rw-r--r-- 1 jailuser jailuser 2120 Nov 26 18:07 AgentLiveSchema.ts
-rw-r--r-- 1 jailuser jailuser  716 Nov 26 18:07 AnalyzeSchema.ts
-rw-r--r-- 1 jailuser jailuser   64 Nov 26 18:07 AsyncAnalyzeResponse.ts
-rw-r--r-- 1 jailuser jailuser   68 Nov 26 18:07 AsyncPrerecordedResponse.ts
-rw-r--r-- 1 jailuser jailuser  145 Nov 26 18:07 CreateOnPremCredentialsSchema.ts
-rw-r--r-- 1 jailuser jailuser  189 Nov 26 18:07 CreateProjectKeyResponse.ts
-rw-r--r-- 1 jailuser jailuser  362 Nov 26 18:07 CreateProjectKeySchema.ts
-rw-r--r-- 1 jailuser jailuser 2581 Nov 26 18:07 DeepgramClientOptions.ts
-rw-r--r-- 1 jailuser jailuser  245 Nov 26 18:07 DeepgramResponse.ts
-rw-r--r-- 1 jailuser jailuser  303 Nov 26 18:07 DeepgramSource.ts
-rw-r--r-- 1 jailuser jailuser  203 Nov 26 18:07 Fetch.ts
-rw-r--r-- 1 jailuser jailuser  347 Nov 26 18:07 FunctionCallResponse.ts
-rw-r--r-- 1 jailuser jailuser  422 Nov 26 18:07 GetModelsResponse.ts
-rw-r--r-- 1 jailuser jailuser   99 Nov 26 18:07 GetModelsSchema.ts
-rw-r--r-- 1 jailuser jailuser  214 Nov 26 18:07 GetProjectBalancesResponse.ts
-rw-r--r-- 1 jailuser jailuser  124 Nov 26 18:07 GetProjectInvitesResponse.ts
-rw-r--r-- 1 jailuser jailuser  381 Nov 26 18:07 GetProjectKeysResponse.ts
-rw-r--r-- 1 jailuser jailuser   72 Nov 26 18:07 GetProjectMemberScopesResponse.ts
-rw-r--r-- 1 jailuser jailuser  191 Nov 26 18:07 GetProjectMembersResponse.ts
-rw-r--r-- 1 jailuser jailuser   97 Nov 26 18:07 GetProjectResponse.ts
-rw-r--r-- 1 jailuser jailuser  271 Nov 26 18:07 GetProjectUsageFieldsResponse.ts
-rw-r--r-- 1 jailuser jailuser  114 Nov 26 18:07 GetProjectUsageFieldsSchema.ts
-rw-r--r-- 1 jailuser jailuser 1109 Nov 26 18:07 GetProjectUsageRequestsResponse.ts
-rw-r--r-- 1 jailuser jailuser  154 Nov 26 18:07 GetProjectUsageRequestsSchema.ts
-rw-r--r-- 1 jailuser jailuser  291 Nov 26 18:07 GetProjectUsageSummaryResponse.ts
-rw-r--r-- 1 jailuser jailuser  543 Nov 26 18:07 GetProjectUsageSummarySchema.ts
-rw-r--r-- 1 jailuser jailuser  125 Nov 26 18:07 GetProjectsResponse.ts
-rw-r--r-- 1 jailuser jailuser   71 Nov 26 18:07 GetTokenDetailsResponse.ts
-rw-r--r-- 1 jailuser jailuser   86 Nov 26 18:07 GrantTokenResponse.ts
-rw-r--r-- 1 jailuser jailuser  226 Nov 26 18:07 GrantTokenSchema.ts
-rw-r--r-- 1 jailuser jailuser  375 Nov 26 18:07 ListOnPremCredentialsResponse.ts
-rw-r--r-- 1 jailuser jailuser  276 Nov 26 18:07 LiveConfigOptions.ts
-rw-r--r-- 1 jailuser jailuser  185 Nov 26 18:07 LiveMetadataEvent.ts
-rw-r--r-- 1 jailuser jailuser  688 Nov 26 18:07 LiveTranscriptionEvent.ts
-rw-r--r-- 1 jailuser jailuser   56 Nov 26 18:07 MessageResponse.ts
-rw-r--r-- 1 jailuser jailuser  111 Nov 26 18:07 SendProjectInviteSchema.ts
-rw-r--r-- 1 jailuser jailuser  625 Nov 26 18:07 SpeakSchema.ts
-rw-r--r-- 1 jailuser jailuser  107 Nov 26 18:07 SpeechStartedEvent.ts
-rw-r--r-- 1 jailuser jailuser 1462 Nov 26 18:07 SyncAnalyzeResponse.ts
-rw-r--r-- 1 jailuser jailuser 3436 Nov 26 18:07 SyncPrerecordedResponse.ts
-rw-r--r-- 1 jailuser jailuser 4890 Nov 26 18:07 TranscriptionSchema.ts
-rw-r--r-- 1 jailuser jailuser  101 Nov 26 18:07 UpdateProjectMemberScopeSchema.ts
-rw-r--r-- 1 jailuser jailuser  110 Nov 26 18:07 UpdateProjectSchema.ts
-rw-r--r-- 1 jailuser jailuser  109 Nov 26 18:07 UtteranceEndEvent.ts
-rw-r--r-- 1 jailuser jailuser  203 Nov 26 18:07 VoidResponse.ts
-rw-r--r-- 1 jailuser jailuser 1842 Nov 26 18:07 index.ts

=== Search for LiveTranscriptionEvents enum ===
./src/lib/enums/index.ts:3:export * from "./LiveTranscriptionEvents";
./src/lib/enums/index.ts-4-export * from "./LiveTTSEvents";
--
./src/lib/enums/LiveTranscriptionEvents.ts:13:export enum LiveTranscriptionEvents {
./src/lib/enums/LiveTranscriptionEvents.ts-14-  /**
./src/lib/enums/LiveTranscriptionEvents.ts-15-   * Built in socket events.
./src/lib/enums/LiveTranscriptionEvents.ts-16-   */
./src/lib/enums/LiveTranscriptionEvents.ts-17-  Open = "open",
./src/lib/enums/LiveTranscriptionEvents.ts-18-  Close = "close",
./src/lib/enums/LiveTranscriptionEvents.ts-19-  Error = "error",
./src/lib/enums/LiveTranscriptionEvents.ts-20-
./src/lib/enums/LiveTranscriptionEvents.ts-21-  /**
./src/lib/enums/LiveTranscriptionEvents.ts-22-   * Message { type: string }
./src/lib/enums/LiveTranscriptionEvents.ts-23-   */
./src/lib/enums/LiveTranscriptionEvents.ts-24-  Transcript = "Results",
./src/lib/enums/LiveTranscriptionEvents.ts-25-  Metadata = "Metadata",
./src/lib/enums/LiveTranscriptionEvents.ts-26-  UtteranceEnd = "UtteranceEnd",
./src/lib/enums/LiveTranscriptionEvents.ts-27-  SpeechStarted = "SpeechStarted",
./src/lib/enums/LiveTranscriptionEvents.ts-28-
./src/lib/enums/LiveTranscriptionEvents.ts-29-  /**
./src/lib/enums/LiveTranscriptionEvents.ts-30-   * Catch all for any other message event
./src/lib/enums/LiveTranscriptionEvents.ts-31-   */
./src/lib/enums/LiveTranscriptionEvents.ts-32-  Unhandled = "Unhandled",
./src/lib/enums/LiveTranscriptionEvents.ts-33-}

=== Search for event listener type definitions ===

stderr:

grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(
grep: Unmatched ( or \(

</shell_result>


Script executed:

#!/bin/bash
# Let me look at the entire ListenLiveClient.ts structure

echo "=== Get line count of ListenLiveClient.ts ==="
wc -l ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Look at lines 150-200 of ListenLiveClient.ts ==="
sed -n '150,200p' ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Search for type/interface definitions in ListenLiveClient ==="
rg -n "interface\|type\|on\(" ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Check for .d.ts files ==="
find ./src -name "*.d.ts" -type f

echo ""
echo "=== Check if there's TypeScript declaration merging for EventEmitter ==="
rg -n "declare\|module augment\|EventEmitter" ./src/packages --type ts -B 1 -A 1

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 1

stdout:

=== Get line count of ListenLiveClient.ts ===
150 ./src/packages/ListenLiveClient.ts

=== Look at lines 150-200 of ListenLiveClient.ts ===
export { ListenLiveClient as LiveClient };

=== Search for type/interface definitions in ListenLiveClient ===

=== Check for .d.ts files ===

=== Check if there's TypeScript declaration merging for EventEmitter ===

</shell_result>


Script executed:

#!/bin/bash
# The file is only 150 lines. Let me see the entire file

echo "=== Read entire ListenLiveClient.ts ==="
cat -n ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Check git history to see if line 156 existed before ==="
cd ./src/packages && git log --oneline ListenLiveClient.ts | head -5
cd - > /dev/null

echo ""
echo "=== Check the PR diff to see what changed ==="
git diff HEAD~1 ./src/packages/ListenLiveClient.ts 2>/dev/null | head -100

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Read entire ListenLiveClient.ts ===
     1	import { AbstractLiveClient } from "./AbstractLiveClient";
     2	import { LiveTranscriptionEvents } from "../lib/enums";
     3	import type { LiveSchema, LiveConfigOptions, DeepgramClientOptions } from "../lib/types";
     4	import { DeepgramError } from "../lib/errors";
     5	
     6	/**
     7	 * The `ListenLiveClient` class extends the `AbstractLiveClient` class and provides functionality for setting up and managing a WebSocket connection for live transcription.
     8	 *
     9	 * The constructor takes in `DeepgramClientOptions` and an optional `LiveSchema` object, as well as an optional `endpoint` string. It then calls the `connect` method of the parent `AbstractLiveClient` class to establish the WebSocket connection.
    10	 *
    11	 * The `setupConnection` method is responsible for handling the various events that can occur on the WebSocket connection, such as opening, closing, and receiving messages. It sets up event handlers for these events and emits the appropriate events based on the message type.
    12	 *
    13	 * The `configure` method allows you to send additional configuration options to the connected session, such as enabling numerals.
    14	 *
    15	 * The `keepAlive` method sends a "KeepAlive" message to the server to maintain the connection.
    16	 *
    17	 * The `requestClose` method requests the server to close the connection.
    18	 *
    19	 * The `finish` method is deprecated as of version 3.4 and will be removed in version 4.0. Use `requestClose` instead.
    20	 */
    21	export class ListenLiveClient extends AbstractLiveClient {
    22	  public namespace: string = "listen";
    23	
    24	  /**
    25	   * Constructs a new `ListenLiveClient` instance with the provided options.
    26	   *
    27	   * @param options - The `DeepgramClientOptions` to use for the client connection.
    28	   * @param transcriptionOptions - An optional `LiveSchema` object containing additional configuration options for the live transcription.
    29	   * @param endpoint - An optional string representing the WebSocket endpoint to connect to. Defaults to `:version/listen`.
    30	   */
    31	  constructor(
    32	    options: DeepgramClientOptions,
    33	    transcriptionOptions: LiveSchema = {},
    34	    endpoint: string = ":version/listen"
    35	  ) {
    36	    super(options);
    37	
    38	    if (transcriptionOptions.keyterm?.length && !transcriptionOptions.model?.startsWith("nova-3")) {
    39	      throw new DeepgramError("Keyterms are only supported with the Nova 3 models.");
    40	    }
    41	
    42	    this.connect(transcriptionOptions, endpoint);
    43	  }
    44	
    45	  /**
    46	   * Sets up the connection event handlers.
    47	   * This method is responsible for handling the various events that can occur on the WebSocket connection, such as opening, closing, and receiving messages.
    48	   * - When the connection is opened, it emits the `LiveTranscriptionEvents.Open` event.
    49	   * - When the connection is closed, it emits the `LiveTranscriptionEvents.Close` event.
    50	   * - When an error occurs on the connection, it emits the `LiveTranscriptionEvents.Error` event.
    51	   * - When a message is received, it parses the message and emits the appropriate event based on the message type, such as `LiveTranscriptionEvents.Metadata`, `LiveTranscriptionEvents.Transcript`, `LiveTranscriptionEvents.UtteranceEnd`, and `LiveTranscriptionEvents.SpeechStarted`.
    52	   */
    53	  public setupConnection(): void {
    54	    // Set up standard connection events (open, close, error) using abstracted method
    55	    this.setupConnectionEvents({
    56	      Open: LiveTranscriptionEvents.Open,
    57	      Close: LiveTranscriptionEvents.Close,
    58	      Error: LiveTranscriptionEvents.Error,
    59	    });
    60	
    61	    // Set up message handling specific to transcription
    62	    if (this.conn) {
    63	      this.conn.onmessage = (event: MessageEvent) => {
    64	        try {
    65	          const data: any = JSON.parse(event.data.toString());
    66	
    67	          if (data.type === LiveTranscriptionEvents.Metadata) {
    68	            this.emit(LiveTranscriptionEvents.Metadata, data);
    69	          } else if (data.type === LiveTranscriptionEvents.Transcript) {
    70	            this.emit(LiveTranscriptionEvents.Transcript, data);
    71	          } else if (data.type === LiveTranscriptionEvents.UtteranceEnd) {
    72	            this.emit(LiveTranscriptionEvents.UtteranceEnd, data);
    73	          } else if (data.type === LiveTranscriptionEvents.SpeechStarted) {
    74	            this.emit(LiveTranscriptionEvents.SpeechStarted, data);
    75	          } else {
    76	            this.emit(LiveTranscriptionEvents.Unhandled, data);
    77	          }
    78	        } catch (error) {
    79	          this.emit(LiveTranscriptionEvents.Error, {
    80	            event,
    81	            message: "Unable to parse `data` as JSON.",
    82	            error,
    83	            url: this.conn?.url,
    84	            readyState: this.conn?.readyState,
    85	            data:
    86	              event.data?.toString().substring(0, 200) +
    87	              (event.data?.toString().length > 200 ? "..." : ""),
    88	          });
    89	        }
    90	      };
    91	    }
    92	  }
    93	
    94	  /**
    95	   * Sends additional config to the connected session.
    96	   *
    97	   * @param config - The configuration options to apply to the LiveClient.
    98	   * @param config.numerals - We currently only support numerals.
    99	   */
   100	  public configure(config: LiveConfigOptions): void {
   101	    this.send(
   102	      JSON.stringify({
   103	        type: "Configure",
   104	        processors: config,
   105	      })
   106	    );
   107	  }
   108	
   109	  /**
   110	   * Sends a "KeepAlive" message to the server to maintain the connection.
   111	   */
   112	  public keepAlive(): void {
   113	    this.send(
   114	      JSON.stringify({
   115	        type: "KeepAlive",
   116	      })
   117	    );
   118	  }
   119	
   120	  /**
   121	   * Sends a "Finalize" message to flush any transcription sitting in the server's buffer.
   122	   */
   123	  public finalize(): void {
   124	    this.send(
   125	      JSON.stringify({
   126	        type: "Finalize",
   127	      })
   128	    );
   129	  }
   130	
   131	  /**
   132	   * @deprecated Since version 3.4. Will be removed in version 4.0. Use `requestClose` instead.
   133	   */
   134	  public finish(): void {
   135	    this.requestClose();
   136	  }
   137	
   138	  /**
   139	   * Requests the server close the connection.
   140	   */
   141	  public requestClose(): void {
   142	    this.send(
   143	      JSON.stringify({
   144	        type: "CloseStream",
   145	      })
   146	    );
   147	  }
   148	}
   149	
   150	export { ListenLiveClient as LiveClient };

=== Check git history to see if line 156 existed before ===
6634e40 chore: adds dx code owners (#430)

=== Check the PR diff to see what changed ===

</shell_result>


Script executed:

#!/bin/bash
# Check if there are any type declaration files or if the review might be from staging

echo "=== Search for any on method with Error listener in entire codebase ==="
rg -n "on.*Error.*listener\|listener.*Error" ./src --type ts -B 2 -A 2

echo ""
echo "=== Check if there's a types declaration for ListenLiveClient event listeners ==="
find ./src -type f -name "*.ts" | xargs grep -l "ListenLiveClient" | xargs grep -l "on\|Error"

echo ""
echo "=== Verify the exact error types being emitted ==="
echo "=== From AbstractLiveClient (setupConnectionEvents line 512) ==="
sed -n '510,515p' ./src/packages/AbstractLiveClient.ts

echo ""
echo "=== From ListenLiveClient (setupConnection line 79) ==="
sed -n '78,90p' ./src/packages/ListenLiveClient.ts

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Search for any on method with Error listener in entire codebase ===

=== Check if there's a types declaration for ListenLiveClient event listeners ===
./src/packages/ListenClient.ts
./src/packages/ListenLiveClient.ts

=== Verify the exact error types being emitted ===
=== From AbstractLiveClient (setupConnectionEvents line 512) ===
        const enhancedInfo = this.extractErrorInformation(event, this.conn || undefined);
        const enhancedError = this.createEnhancedError(event, enhancedInfo);
        this.emit(events.Error, enhancedError);
      };
    }
  }

=== From ListenLiveClient (setupConnection line 79) ===
        } catch (error) {
          this.emit(LiveTranscriptionEvents.Error, {
            event,
            message: "Unable to parse `data` as JSON.",
            error,
            url: this.conn?.url,
            readyState: this.conn?.readyState,
            data:
              event.data?.toString().substring(0, 200) +
              (event.data?.toString().length > 200 ? "..." : ""),
          });
        }
      };

</shell_result>


Script executed:

#!/bin/bash
# Check createEnhancedError return type

echo "=== Check createEnhancedError return type ==="
sed -n '405,425p' ./src/packages/AbstractLiveClient.ts

echo ""
echo "=== Check DeepgramWebSocketError structure ==="
cat -n ./src/lib/errors.ts | sed -n '56,80p'

echo ""
echo "=== Check if there's any type information in comments or code ==="
rg -n "DeepgramApiError\|DeepgramWebSocketError" ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Double-check imports in ListenLiveClient ==="
head -5 ./src/packages/ListenLiveClient.ts

echo ""
echo "=== Check ListenClient.ts to see if it has anything relevant ==="
head -100 ./src/packages/ListenClient.ts | tail -50

Repository: deepgram/deepgram-js-sdk


<shell_result>
Repository: deepgram/deepgram-js-sdk
Exit code: 0

stdout:

=== Check createEnhancedError return type ===
  protected createEnhancedError(
    event: ErrorEvent | Event,
    enhancedInfo: {
      statusCode?: number;
      requestId?: string;
      responseHeaders?: Record<string, string>;
      url?: string;
      readyState?: number;
    }
  ) {
    // Create the enhanced error for detailed debugging
    const enhancedError = new DeepgramWebSocketError(
      (event as ErrorEvent).message || "WebSocket connection error",
      {
        originalEvent: event,
        ...enhancedInfo,
      }
    );

    // Return an object that maintains backward compatibility
    // while providing enhanced information

=== Check DeepgramWebSocketError structure ===
    56	export class DeepgramWebSocketError extends DeepgramError {
    57	  originalEvent?: ErrorEvent | Event;
    58	  statusCode?: number;
    59	  requestId?: string;
    60	  responseHeaders?: Record<string, string>;
    61	  url?: string;
    62	  readyState?: number;
    63	
    64	  constructor(
    65	    message: string,
    66	    options: {
    67	      originalEvent?: ErrorEvent | Event;
    68	      statusCode?: number;
    69	      requestId?: string;
    70	      responseHeaders?: Record<string, string>;
    71	      url?: string;
    72	      readyState?: number;
    73	    } = {}
    74	  ) {
    75	    super(message);
    76	    this.name = "DeepgramWebSocketError";
    77	    this.originalEvent = options.originalEvent;
    78	    this.statusCode = options.statusCode;
    79	    this.requestId = options.requestId;
    80	    this.responseHeaders = options.responseHeaders;

=== Check if there's any type information in comments or code ===

=== Double-check imports in ListenLiveClient ===
import { AbstractLiveClient } from "./AbstractLiveClient";
import { LiveTranscriptionEvents } from "../lib/enums";
import type { LiveSchema, LiveConfigOptions, DeepgramClientOptions } from "../lib/types";
import { DeepgramError } from "../lib/errors";


=== Check ListenClient.ts to see if it has anything relevant ===
import { AbstractClient } from "./AbstractClient";
import { ListenLiveClient } from "./ListenLiveClient";
import { ListenRestClient } from "./ListenRestClient";
import { LiveSchema } from "../lib/types";

/**
 * The `ListenClient` class extends the `AbstractClient` class and provides access to the "listen" namespace.
 * It exposes two methods:
 *
 * 1. `prerecorded()`: Returns a `ListenRestClient` instance for interacting with the prerecorded listen API.
 * 2. `live(transcriptionOptions: LiveSchema = {}, endpoint = ":version/listen")`: Returns a `ListenLiveClient` instance for interacting with the live listen API, with the provided transcription options and endpoint.
 */
export class ListenClient extends AbstractClient {
  public namespace: string = "listen";

  /**
   * Returns a `ListenRestClient` instance for interacting with the prerecorded listen API.
   */
  get prerecorded() {
    return new ListenRestClient(this.options);
  }

  /**
   * Returns a `ListenLiveClient` instance for interacting with the live listen API, with the provided transcription options and endpoint.
   * @param {LiveSchema} [transcriptionOptions={}] - The transcription options to use for the live listen API.
   * @param {string} [endpoint=":version/listen"] - The endpoint to use for the live listen API.
   * @returns {ListenLiveClient} - A `ListenLiveClient` instance for interacting with the live listen API.
   */
  public live(
    transcriptionOptions: LiveSchema = {},
    endpoint: string = ":version/listen"
  ): ListenLiveClient {
    return new ListenLiveClient(this.options, transcriptionOptions, endpoint);
  }
}

</shell_result>


Based on my verification, I can now generate the corrected review comment. The review identified a real issue but with significant inaccuracies:

Error event emissions have inconsistent types across the class hierarchy.

The Error event emits two different error structures:

  1. From AbstractLiveClient.setupConnectionEvents (line 512): Emits DeepgramWebSocketError with properties {originalEvent, statusCode, requestId, responseHeaders, url, readyState}
  2. From ListenLiveClient.setupConnection (lines 79-88): Emits a custom object with {event, message, error, url, readyState, data}

This creates a type safety issue where event listeners receive inconsistent error shapes. Since the on() method is inherited from Node's EventEmitter (which uses generic typing), there's no type safety enforcing consistency.

Consider standardizing the error emission to always emit DeepgramWebSocketError, or define a proper union type that handles both error scenarios:

type LiveTranscriptionError = DeepgramWebSocketError | {
  event: MessageEvent;
  message: string;
  error: unknown;
  url?: string;
  readyState?: number;
  data?: string;
};

Then apply consistent typing to error emissions throughout both classes.

🤖 Prompt for AI Agents
In src/packages/ListenLiveClient.ts around line 156, the Error event emits
inconsistent error shapes across the class hierarchy which breaks type safety;
standardize by defining a single error type (either use DeepgramWebSocketError
everywhere or create a union type LiveTranscriptionError that includes both
shapes) and update the EventEmitter typings and all emit sites to use that type
consistently (modify ListenLiveClient.setupConnection emits and
AbstractLiveClient.setupConnectionEvents emits to match the chosen type, and
update the on(...) signature to accept the unified type).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant